@entrorove/skill 0.1.69 → 0.1.70

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.
@@ -0,0 +1,101 @@
1
+ // Interpret a bounded subset of uni-app conditions; never evaluate source code.
2
+ // Grammar and macro families: https://uniapp.dcloud.net.cn/tutorial/platform.html
3
+ const platforms = ['H5', 'APP-PLUS', 'APP-HARMONY', 'APP-ANDROID', 'APP-IOS', 'MP-WEIXIN', 'MP-ALIPAY',
4
+ 'MP-BAIDU', 'MP-TOUTIAO', 'MP-LARK', 'MP-QQ', 'MP-KUAISHOU', 'MP-JD', 'MP-360', 'MP-HARMONY',
5
+ 'MP-XHS', 'QUICKAPP-NATIVE', 'QUICKAPP-WEBVIEW-UNION', 'QUICKAPP-WEBVIEW-HUAWEI'];
6
+ const flags = ['WEB', 'APP', 'APP-VUE', 'APP-NVUE', 'APP-PLUS-NVUE', 'APP-UVUE', 'MP',
7
+ 'QUICKAPP-WEBVIEW', 'VUE2', 'VUE3', 'VUE3-VAPOR', 'UNI-APP-X'];
8
+ const known = new Set([...platforms, ...flags]);
9
+ export function uniConditionalError(filename, offset, reason, source = '') {
10
+ const prefix = (typeof source === 'string' ? source : '').slice(0, Math.max(0, offset)), lines = prefix.split(/\r\n|\r|\n/);
11
+ const line = lines.length, column = lines.at(-1).length + 1;
12
+ return Object.assign(new Error(`${filename}:${line}:${column}: ${reason}`), {
13
+ code: 'ENTROROVE_IMPORT_UNSUPPORTED', path: filename, line, column, loc: { start: { offset } } });
14
+ }
15
+ export function uniCondition(expression, fail) {
16
+ if (expression.length > 512) throw fail('Conditional expression exceeds its 512-character bound');
17
+ const tokens = expression.match(/uniVersion|[A-Za-z_][A-Za-z0-9_-]*|\d+(?:\.\d+)?|\|\||&&|>=|<=|===|!==|==|!=|[!()<>]|\S/g) ?? [];
18
+ if (!tokens.length || tokens.length > 128) throw fail('Conditional expression requires a bounded supported expression');
19
+ let cursor = 0, depth = 0;
20
+ const versions = [], macros = new Set();
21
+ const atom = () => {
22
+ if (++depth > 24) throw fail('Conditional expression exceeds its nesting bound');
23
+ let result; const token = tokens[cursor++];
24
+ if (token === '!') result = { kind: 'not', value: atom() };
25
+ else if (token === '(') { result = disjunction(); if (tokens[cursor++] !== ')') throw fail('Conditional parentheses are not balanced'); }
26
+ else if (token === 'uniVersion') {
27
+ const operator = tokens[cursor++], literal = tokens[cursor++], value = Number(literal);
28
+ if (!['>', '>=', '<', '<=', '==', '===', '!=', '!=='].includes(operator)
29
+ || !/^\d+(?:\.\d+)?$/.test(literal ?? '') || !Number.isFinite(value) || value > 1_000_000) {
30
+ throw fail('uniVersion requires a supported comparison with a finite nonnegative version literal');
31
+ }
32
+ versions.push(value); result = { kind: 'version', operator, value };
33
+ } else {
34
+ const name = token?.replaceAll('_', '-');
35
+ if (!known.has(name)) throw fail('Conditional macro is unknown; its build definition cannot be proven by this scanner');
36
+ macros.add(name); result = { kind: 'macro', name };
37
+ }
38
+ depth--; return result;
39
+ };
40
+ const conjunction = () => {
41
+ let left = atom();
42
+ while (tokens[cursor] === '&&') { cursor++; left = { kind: 'and', left, right: atom() }; }
43
+ return left;
44
+ };
45
+ const disjunction = () => {
46
+ let left = conjunction();
47
+ while (tokens[cursor] === '||') { cursor++; left = { kind: 'or', left, right: conjunction() }; }
48
+ return left;
49
+ };
50
+ const tree = disjunction();
51
+ if (cursor !== tokens.length) throw fail('Conditional expression uses unsupported syntax');
52
+ return { tree, versions, macros: [...macros], tokens: tokens.length };
53
+ }
54
+ export function evaluateUniCondition(node, context) {
55
+ if (node.kind === 'macro') return context.macros.has(node.name);
56
+ if (node.kind === 'not') return !evaluateUniCondition(node.value, context);
57
+ if (node.kind === 'and') return evaluateUniCondition(node.left, context) && evaluateUniCondition(node.right, context);
58
+ if (node.kind === 'or') return evaluateUniCondition(node.left, context) || evaluateUniCondition(node.right, context);
59
+ const a = context.version, b = node.value;
60
+ return ({ '>': () => a > b, '>=': () => a >= b, '<': () => a < b, '<=': () => a <= b,
61
+ '==': () => a === b, '===': () => a === b, '!=': () => a !== b, '!==': () => a !== b })[node.operator]();
62
+ }
63
+ function versionSamples(conditions, fail) {
64
+ const values = [...new Set(conditions.flatMap(condition => condition.versions))].sort((a, b) => a - b);
65
+ if (values.length > 8) throw fail('Conditional versions exceed their eight-boundary inspection limit');
66
+ const samples = new Set([0, ...values]);
67
+ for (let index = 0; index < values.length; index++) {
68
+ const previous = values[index - 1] ?? 0;
69
+ if (values[index] > previous) samples.add(previous + (values[index] - previous) / 2);
70
+ }
71
+ if (values.length) samples.add(values.at(-1) + 1);
72
+ return [...samples];
73
+ }
74
+ function platformMacros(platform, engine) {
75
+ const macros = new Set([platform, engine.endsWith('2') ? 'VUE2' : 'VUE3']);
76
+ if (platform === 'H5') macros.add('WEB');
77
+ if (platform.startsWith('MP-')) macros.add('MP');
78
+ if (platform.startsWith('QUICKAPP-WEBVIEW')) macros.add('QUICKAPP-WEBVIEW');
79
+ if (platform.startsWith('APP-')) {
80
+ macros.add('APP'); macros.add(engine.startsWith('x') ? 'APP-UVUE' : engine.startsWith('nvue') ? 'APP-NVUE' : 'APP-VUE');
81
+ if (engine.startsWith('nvue')) macros.add('APP-PLUS-NVUE');
82
+ if (platform !== 'APP-HARMONY' && !engine.startsWith('x')) macros.add('APP-PLUS');
83
+ if (!engine.startsWith('x')) { macros.delete('APP-ANDROID'); macros.delete('APP-IOS'); }
84
+ }
85
+ if (engine.startsWith('x')) macros.add('UNI-APP-X');
86
+ if (engine === 'x-vapor') macros.add('VUE3-VAPOR');
87
+ return macros;
88
+ }
89
+ export function uniConditionContexts(conditions, fail) {
90
+ const samples = versionSamples(conditions, fail), output = [], seen = new Set();
91
+ for (const platform of platforms) for (const engine of ['vue2', 'vue3', 'nvue2', 'nvue3', 'x', 'x-vapor']) for (const version of samples) {
92
+ if (engine.startsWith('nvue') && !platform.startsWith('APP-')) continue;
93
+ const context = { macros: platformMacros(platform, engine), version, label: `${platform}/${engine}` };
94
+ const key = conditions.map(condition => evaluateUniCondition(condition.tree, context) ? '1' : '0').join('');
95
+ if (!seen.has(key)) {
96
+ seen.add(key); output.push({ ...context, key });
97
+ if (output.length > 64) throw fail('Conditional compilation exceeds its 64-variant inspection bound');
98
+ }
99
+ }
100
+ return output;
101
+ }
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { promises as fs } from 'node:fs';
3
3
  import { scopeFail, scopePath, scopeRead, scopeTarget } from './local-scope-files.mjs';
4
+ import { observeScopeDirectory } from './local-scope-read-set.mjs';
4
5
 
5
6
  async function manifest(root, relative, input) {
6
7
  try {
@@ -32,14 +33,25 @@ async function patterns(root, input) {
32
33
  } catch (error) { if (error.code !== 'ENOENT') throw error; }
33
34
  return { patterns: [...new Set(result)], metadata };
34
35
  }
36
+ async function directoryNames(root, parent) {
37
+ const target = await scopeTarget(root, parent);
38
+ if (!target.stat.isDirectory()) scopeFail('ENTROROVE_PACKAGE_INVALID', 'workspace parent must be a real directory');
39
+ const names = [], directory = await fs.opendir(target.target);
40
+ for await (const entry of directory) {
41
+ if (names.length >= 10_000) scopeFail('ENTROROVE_SCOPE_LIMIT', 'workspace directory exceeds its bounded entry inspection');
42
+ names.push(entry.name);
43
+ }
44
+ return names.sort();
45
+ }
35
46
  async function packageDirectories(root, pattern) {
36
47
  if (!pattern.includes('*')) { scopePath(pattern); return [pattern]; }
37
48
  if (!pattern.endsWith('/*') || pattern.slice(0, -2).includes('*')) scopeFail('ENTROROVE_PACKAGE_INVALID', 'workspace glob needs a literal parent and a single terminal wildcard');
38
49
  const parent = scopePath(pattern.slice(0, -2)); const item = await scopeTarget(root, parent, true);
39
50
  if (!item.exists) return [];
40
51
  if (!item.stat.isDirectory()) scopeFail('ENTROROVE_PACKAGE_INVALID', 'workspace parent must be a real directory');
41
- const result = [];
42
- for (const name of (await fs.readdir(item.target)).sort()) {
52
+ const result = [], names = await directoryNames(root, parent);
53
+ observeScopeDirectory(root, parent, names, () => directoryNames(root, parent));
54
+ for (const name of names) {
43
55
  const directory = `${parent}/${name}`; const target = await scopeTarget(root, directory);
44
56
  if (target.stat.isDirectory()) result.push(directory);
45
57
  }
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "entrorove.skill-manifest/1.0",
3
3
  "name": "entrorove",
4
- "version": "0.1.69",
4
+ "version": "0.1.70",
5
5
  "endpoint": "https://entrorove.com",
6
6
  "requestSchemaVersion": "entrorove.skill.request/2.0",
7
7
  "supportedRequestSchemaVersions": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entrorove/skill",
3
- "version": "0.1.69",
3
+ "version": "0.1.70",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "entrorove": "cli.mjs"
@@ -311,7 +311,15 @@
311
311
  "package-archive.mjs",
312
312
  "package-replacement.mjs",
313
313
  "rollback-history.mjs",
314
- "rollback-runner.mjs"
314
+ "rollback-runner.mjs",
315
+ "scope-context-journal.mjs",
316
+ "cloud-locale.mjs",
317
+ "cloud-locale.d.mts",
318
+ "local-scope-uni-expressions.mjs",
319
+ "local-scope-uni-directives.mjs",
320
+ "local-scope-uni-conditional.mjs",
321
+ "local-scope-uni-build.mjs",
322
+ "local-scope-read-set.mjs"
315
323
  ],
316
324
  "scripts": {
317
325
  "test": "node test/run.mjs",
@@ -1,15 +1,18 @@
1
1
  {
2
2
  "schemaVersion": "entrorove.release-state/1.0",
3
- "version": "0.1.69",
4
- "digest": "4302b1f59039183c75228114755903be6ee5974bfdb95b6f7f72596f1039ff58",
3
+ "version": "0.1.70",
4
+ "digest": "888f8c2a60595e87e39b9423f811a0e231ec5309094f3de0d88cb3f729371ba8",
5
5
  "files": {
6
- "scope-context-client.mjs": "a21cfe75b3e13a5c5c0619cf14c84c579289d98221c6e280019dbecebfb444b9",
7
- "scope-context-observer.mjs": "533d5096fd7f17ec255b2a7b308162e093179c1bb1f1db976aef072b8ddae76f",
6
+ "scope-context-client.mjs": "dbfec7db0e118f3025a0e45475f3328a171e4cd1dd86e6c8d326d779ce61b459",
7
+ "scope-context-observer.mjs": "9d041db8de6e45113ee04b189c9d52097ec902a104721829dd382cf6daf5d1ce",
8
8
  "scope-context-observation.mjs": "d92f2fca3c923caa6f0c5c9cc60b4f12bdae212071da64da6692e3e99b251969",
9
9
  "scope-context-remote.mjs": "b52cdee03e845edc15eae6bb3ccfaea9c4482c03f2fae7001439e4abcb2acd33",
10
10
  "scope-context-state.mjs": "7bc434620bfc505ee290f1977e64ef22e50f72665d75e7dfd6805deab4e81826",
11
11
  "scope-context-wire.mjs": "a15d0b966615b8293e53e4229edc8fb444fb144cc336dbd6354ac995f33e8cc8",
12
- "scope-context-release.mjs": "32238648219cd35a9b2c4bc01784d2150af57df16da2975fd93e94ab4f3e6065",
12
+ "scope-context-journal.mjs": "753821f546e36185b2e891e8f38758a2d15a591f700f0650d478892c8d958a45",
13
+ "scope-context-release.mjs": "3b6f5d3bf7abe25e1a1edd0521053f4c1b1e3487b7f37d5f7c6080d32bc0c044",
14
+ "cloud-locale.mjs": "0627ac75b98b439563b61ab217aacb55a099c1d0c8ed3b118776d2007c52dd70",
15
+ "cloud-locale.d.mts": "aff4bced9bbac140f73fd0de24681287349389be5750d89d96abe0f8a2167664",
13
16
  "cloud-task-images.mjs": "a260a94b6b1b47df97a8cc8657dfdce1fd37ec752f1cc2bad2542eaa28622bdb",
14
17
  "model-images.mjs": "ab85ac0c44d857c1f460de3e9c06eb1d37b57688b4a6061405c0a44ae4d40bd7",
15
18
  "model-images.d.mts": "f1506cfb8ad287869f92f4d9074c7c759c6c28b182aeb325dd5f22a38926f24c",
@@ -41,7 +44,7 @@
41
44
  "local-preserved-mutation-signer.mjs": "2b0771cbdd18ba00267aa441b7892cd04feab9f8543ae5e2703430b1e580747f",
42
45
  "local-preserved-mutation-state.mjs": "337a172b2d0a03b0d5a89b8483afdd9a043a4999fe38f87679633c98c450126b",
43
46
  "local-preserved-mutation-wire.mjs": "6768ce5713d70f7c0bd05f2286bc4b32cd9b8bb1d3c8b9cc7a451cb9b176824d",
44
- "local-delegated-modify-analysis.mjs": "f87148ddb023b79e241ca97db582991da8d0e1a51b26b1d9bc2799a76efd7297",
47
+ "local-delegated-modify-analysis.mjs": "f8769bd9890a041be48db5cb6724873d59300c102a5ddaf0a937e5e4f3f1983a",
45
48
  "mutation-client.mjs": "8f1066db3246f7639dd0b557e7b2c6175181e52f9087146e218d0912f791808e",
46
49
  "cloud-mutation-authority.mjs": "c7226e882a4f3be4d2f55728f515d5a815c0aa1f21aa5e4689314e6e6a8c66a2",
47
50
  "ide-code-mode.mjs": "f8ba4de33632ab4c6df52dab52f392414b4632c7e94b218b8065677970c075c7",
@@ -73,7 +76,7 @@
73
76
  "runtime/validator-runtime-v7.0.44.d.mts": "7797edbfe2f7de24da370204dce4dfd22822a1bd80df93514fff900b98afe36f",
74
77
  "snapshot-retention.mjs": "ca46aeb36b1a5a24fe7e967031a71b9a3d33f40cb715d344402cbe0dc9c425cb",
75
78
  "snapshot-cleanup-journal.mjs": "76a9684cc95b4eb076e1298d78a9dff37948b00d9607b8f546c599072d3ed331",
76
- "IDE-EXECUTION.md": "1b58e8ef0c4c95606184ba24bd0b674e3f318000d4164ec40989bda5e1eb645f",
79
+ "IDE-EXECUTION.md": "26d4f32de1694c7f29ceefc5a5e5f4bba717efc5f91c37a91c399faa1574a191",
77
80
  "REUSE-DISCIPLINE.md": "a2452911c2b50c4762b4a1374f1958059eb03efd59f63e1b50e1650c52b0dc98",
78
81
  "install-identity.mjs": "41b8c8d8b3e265d2057a07bf7dc98066c34d6b05c65bf7159ca9609c01ee4646",
79
82
  "package-archive.mjs": "e06db8d709db8710bead254ee950b78928088be01c58dcea383beaa5eb574e59",
@@ -91,7 +94,7 @@
91
94
  "design-declaration.mjs": "f6c7f56e4697a05a0a832fcdb5a3b07ffe60aa602ba0681cfd09948bdcafc8ad",
92
95
  "design-frameworks.mjs": "5d09a8e56068302f951a6236f36cb50e655fb6d6ffbeb2e4eb4b19f025c8a411",
93
96
  "design-gate.mjs": "567eb666937704123ef72f26ad1145d9d32f7dfde59553381286de083e4146cb",
94
- "design-lint.mjs": "9e27d0a6041fbb7f7f2b88b0c46d69dd790582426b534ad436b151d81512a66e",
97
+ "design-lint.mjs": "76adb33d0faf0cfcdef7583a2f4835eccc7e49811480e2fd2b09779a81c0cbab",
95
98
  "design-local-contracts.mjs": "f331bd1d21f30994596bd9c46008e551f3ed2b6d5eea19d4546c7da0663b768a",
96
99
  "design-operations.mjs": "e21e9fcf9fd16115c72558c7cd7a57f7213a30934bde0f1b649aec647d1205f7",
97
100
  "design-policy.d.mts": "48fed97fca0f97fef786f182f666d7e6e33efb814cda1991239e1c38984cf80e",
@@ -149,13 +152,13 @@
149
152
  "install-cleanup.mjs": "8a8e1a7dfe37427f86cd4d6e317890d0829894a9949d4216674a716b36edbc54",
150
153
  "review-client.mjs": "3491eef68ed533174b31db3c53febb6519b8f088f71a9964644714a9a4c03c77",
151
154
  "version.mjs": "85284d4cd5e3314b449e6ae124243f96397bcc274817b5983a79cfdd903d46fd",
152
- "SKILL.md": "4722190a3110d40ab7b18dcdcdf53bd6b378f5dd6015c7e6855773456d236198",
155
+ "SKILL.md": "db0ff568f83086226044726c37a0f8de517042aece8fb1676f1830c9e32f8d79",
153
156
  "CAPABILITY-MAP.md": "517809bb20750e5de89b2dab5705c902a27fa68cf4f3f392a5d8cf2f19818888",
154
157
  "AGREEMENT.md": "f93a68e92c457200dd024610e65f74e478b8f60860f951da818b70561f1b9bf8",
155
158
  "DELIVERY-BOUNDARIES.md": "cb23ce71b2197085c8441ba7c5b6c05fb21f2a954bf66b4d60cfef8b15895b8b",
156
- "package.json": "68c2b335cd192d783a516de6b5449e284e16a1c3eb4adb4e7717f5d002282dcc",
159
+ "package.json": "71c16c55000a8d0d7cca2b935ceba15799b3e8d15be2081a7e077ce803d7b319",
157
160
  "manifest.json": "ab06e307795bc9da5add926752b6c4dbdaf18625090261a035aa356a679c504f",
158
- "release.mjs": "575204085e0595cc7073a78f321d8c9243cd2c0910a6d15951efe8d93ba77512",
161
+ "release.mjs": "2250561b382b61aa1f760705c872d7248266a69a96cc30e8957f42e9e48649e5",
159
162
  "local-cli.mjs": "058ac9a4653bc12e0d031b72034ebfe6a72b91b9b23353b9fee04881c7d2e098",
160
163
  "local-state-lock.mjs": "1dbf64264642a0851faf043e07ee9f66d2ad0508e9f799b0626246e42fae893e",
161
164
  "local-execution.mjs": "721ed7d29bbe4ce6d088909cd84ffc440978c3c9eb0946c49a4281ee685fe3aa",
@@ -193,8 +196,13 @@
193
196
  "local-stage-boundary.mjs": "84c3bb5beab29306274c5ca8a78f19a83ed664ae1b93a8372bb493158b304fe3",
194
197
  "local-scope-candidate.mjs": "8f0bd9606e70d4a0ebacdd3cd600ec3afed85e2bb3e1760aa1144cf55d4e028a",
195
198
  "local-scope-context.mjs": "79753abfc6c76089d17f003a1be537dfaea10d6731c542f7fc53f2a9996ed164",
196
- "local-scope-files.mjs": "db8b3baf8118c6cfd5525cbc008e054f9f33390b9ffce5ba72ee986b832f33e3",
197
- "local-scope-repository.mjs": "df584d45473353a19d9b5238c814456362f3004c9db183fca66f64c1fa86ec34",
199
+ "local-scope-files.mjs": "4793c2a1b458346bd9099e74bbfa0623024c423e754f65ae9e01ca388b49fd36",
200
+ "local-scope-repository.mjs": "1e0c1aef43929237226145d3ba776facaa7e3fc89eb8f88af0a2e5ab38bcdde9",
201
+ "local-scope-read-set.mjs": "a5137d2e7ecffe669143fb51f0f3fba7526a881b512269858ea1e3504d9a50b2",
202
+ "local-scope-uni-expressions.mjs": "200f3f5284125c2674d9e797a7550e7bb7700932db237e1df2d21c3a209ac924",
203
+ "local-scope-uni-directives.mjs": "9951af9f98de5299bad636a09cd7f4e129c96d20939650375f7f677f7919e0c6",
204
+ "local-scope-uni-conditional.mjs": "8a4d03939e1daf8d257b10b44b3808b84404433113c39eff32c8643d7f9f7b18",
205
+ "local-scope-uni-build.mjs": "9cc45336ad6807a73c6e65d2b23f0c6606e22a423f8105710e532b50f76896c5",
198
206
  "local-scope-create-require.mjs": "6b6a209bfcad8fe1398e99c5c8cc3af189c120b9c38e244a73dd1a99c3d91295",
199
207
  "local-scope-require-resolution.mjs": "b24596462c8776cbefa6f0a74749d665aa928d1643c68d44798eb93e2dd5ad42",
200
208
  "local-scope-git.mjs": "53bbbb6c4795db711b5bc164591a6ad79a561427da046d6bbe997dae6eb82829",
@@ -220,15 +228,15 @@
220
228
  "local-budget-git-system-attributes.mjs": "367a4e5a88490c4d4c3717bc893a271970fe70d7705f0c1c72d6f76a442d5d21",
221
229
  "runtime/git-native-linux-v1.mjs": "cf5cb91426a105e62c0f07a8a230dec592dba46f85a3f708b4c4e8cfcd1126c6",
222
230
  "runtime/git-native-darwin-v1.mjs": "f88aab72dc86f94eb1115784872957cb8ed0f74205ae70e1ecd1939e7790e310",
223
- "local-scope-signature.mjs": "99b7d9e563d897c1f1bcf70894cd6370dd41ddf9a136c20822083481ef706fca",
231
+ "local-scope-signature.mjs": "5aba6422031873946874307175b5a6f1fa609faa8160e1a1bb8db44c02c58d18",
224
232
  "local-scope-config-imports.mjs": "a399407227bbdd14468633bb6d25586df7c6a30a1ef0d1a80ab7c7b499d3fadf",
225
233
  "local-scope-config-extends.mjs": "3ab4150b846b247e9e0851221123f220c5574699af651830e751f026ab33659e",
226
234
  "local-scope-snapshot.mjs": "c0f851470b98863a1d187825ed61ee6072d1c057b178fdc85ae29af02a3b250f",
227
235
  "local-scope-state.mjs": "8b600a91fc0068d7a8c0bb752ddba7ed30ada93daafb118d6cba2368fd94cb27",
228
- "local-scope-workspaces.mjs": "a44c34537e5ed2dcaf193a985e5bc05957989fbb160a650f9e37ba7d66eb2de8",
236
+ "local-scope-workspaces.mjs": "7484905ab0434e1a98f95038c65003ae48e395e9cfae07a4322f481537a08392",
229
237
  "local-task-routing.mjs": "6e2e506b2fb0e2e8d8c5147d7df32849b57a4e94a23634f01a355d45735858e4",
230
238
  "remote-client.mjs": "d30fd61fab3171a9e41546706a57ae372786d20ea9e9c01cad39d9863c156b40",
231
- "cloud-task-client.mjs": "603a7de8e1d56a77588f08f7d1ae93685b553b40815e268d7bd17018eb767c37",
239
+ "cloud-task-client.mjs": "f9085c5e6af6f179282670d2e872b85bfabac2657d3d232b1246dc23f5a6ccdd",
232
240
  "scope-runner-client.mjs": "b85999bca05efd804325f97b6e5704623ac3fcf751c6f9760717a23f40661ad1",
233
241
  "scope-amendment-client.mjs": "64a38deddc2dbfae543fead2cff8c9c78771b576391cc903aa58db925a138ad7",
234
242
  "scope-amendment-state.mjs": "922c4fd5a71101651d40fdb11f94f09c904cc244ff2574ae6b6ee004448aad06",
@@ -240,9 +248,9 @@
240
248
  "native-client.mjs": "6f5575909bae9daba643287f876ffa0f9ff1c53a367b32a40145d740456d6605",
241
249
  "native-notification.mjs": "8859a26c6f13d222711f763acf49ed8546b3306cf71f990643ac31a5ccaf1ba1",
242
250
  "local-peer-status.mjs": "a971bcc69d60bb32b875ca5bb4297d948631d5d2821b812c279613ea059a8b4f",
243
- "cloud-watch-client.mjs": "5b93935c19a104d72afed07d4a56746c9617707e075884b93d22050c95cee0c1",
251
+ "cloud-watch-client.mjs": "2042392cd62f1596a35ef3f6cb376f8ed67790351576b007e32f134ce018aec1",
244
252
  "cloud-event-stream.mjs": "e0eec9fc3e0613094523929634f25030df5ca5c5b5fe2c1e1a4586ab10574808",
245
- "ide-presentation.mjs": "3bac21bfc824fcdad63aafb0d7c3326cd6456923f4224106dd5c38682060f315",
253
+ "ide-presentation.mjs": "d826afe0dfcc38906ba76dcbbe021807731587c38026148624546c334057e7f3",
246
254
  "automatic-feedback-client.mjs": "532312d29e66fa211ea3fe879d9add1dd48841ee5ce82800b4b06252a160a255",
247
255
  "task-entry-client.mjs": "940fb068afe28a2a34fe96e285e149afdd9ba79649c0c65cde37ec4ed71ae39d",
248
256
  "task-identity.mjs": "e0e6cf1f48bfa5ee3c5cb86a3c21adfce6d66b4220f4a164ca099263c7a09fb4",
@@ -271,7 +279,7 @@
271
279
  "local-contract-types.mjs": "85e71fa4dd67e351a4b67573c740bcdb1661cede53f097189f11837771e75e91",
272
280
  "local-archguard-ast.mjs": "7ff7df9dab0d98ed62983add751c80edea598bc94ef4f38ede0ebd4d41226c87",
273
281
  "local-archguard-checkpoint.mjs": "37896902520cc995720c96288e4a5ed3585952b55c9c35be281f91fa754c71ec",
274
- "local-archguard-components.mjs": "a8fb3d0fd787f9160905013fd333cc0b9e8cf624cce797461f36f4ab31d170ad",
282
+ "local-archguard-components.mjs": "07d0554355eebba7989b373d019682d194a0d37a031abbcc6b4b24b19f0a75e8",
275
283
  "local-archguard-contract.mjs": "cfc397a1ce171ab84f8bf1db152e6d8d280b7ca041da56a7a4cd62b02c2e2e89",
276
284
  "local-archguard-contracts.mjs": "29e9b86f14dc086aa0b791c874d6efc50d849f806b3f2b8725481114f01ba8ba",
277
285
  "local-archguard-evaluate.mjs": "c34f0956e7a4f1afaa1554c03e238a350dc8a981dc595cf4ed065fb14f733f93",
package/release.mjs CHANGED
@@ -49,7 +49,7 @@ const memberContinuationSourceFiles = Object.freeze([
49
49
  'workflow-observation-wire.mjs',
50
50
  ])
51
51
  const sourceFiles = [
52
- ...SCOPE_CONTEXT_SOURCE_FILES, 'cloud-task-images.mjs', 'model-images.mjs', 'model-images.d.mts', 'cli-input.mjs',
52
+ ...SCOPE_CONTEXT_SOURCE_FILES, 'cloud-locale.mjs', 'cloud-locale.d.mts', 'cloud-task-images.mjs', 'model-images.mjs', 'model-images.d.mts', 'cli-input.mjs',
53
53
  'scope-amendment-retirement.mjs', 'scope-amendment-consumption.mjs', 'mutation-connection-state.mjs', 'mutation-challenge-refresh.mjs',
54
54
  'local-scope-font.mjs', 'local-scope-zip.mjs', 'member-producer-repair-authority.mjs',
55
55
  'local-mutation-completion-evidence.mjs', 'local-mutation-environment-observation.mjs', 'local-mutation-finder-observation.mjs', 'local-finder-store.mjs', 'local-finder-plist.mjs',
@@ -91,7 +91,7 @@ const sourceFiles = [
91
91
  'local-scope-candidate.mjs',
92
92
  'local-scope-context.mjs',
93
93
  'local-scope-files.mjs',
94
- 'local-scope-repository.mjs',
94
+ 'local-scope-repository.mjs', 'local-scope-read-set.mjs', 'local-scope-uni-expressions.mjs', 'local-scope-uni-directives.mjs', 'local-scope-uni-conditional.mjs', 'local-scope-uni-build.mjs',
95
95
  'local-scope-create-require.mjs',
96
96
  'local-scope-require-resolution.mjs',
97
97
  'local-scope-git.mjs',
package/releases.json CHANGED
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.1.70",
4
+ "date": "2026-10-20",
5
+ "zh": "云端沙箱固定中英文输出语言,界面、通知和 Blueprint 文档正文按对应语种显示,原始证据单列来源并保留旧流程精确版本。修复 uni-app 默认条件编译分支解析与长扫描后的原任务签名挑战衔接,核对真实读集并保留原审批、完整路径、快照及失败历史。",
6
+ "en": "Freeze cloud sandbox output language across presentation, notifications and Blueprint document prose; preserve original source evidence and exact historical runtimes. Fix default uni-app conditional branch inspection and renew the original signing challenge after long scans, rechecking actual reads while retaining approvals, complete paths, snapshots and failure history."
7
+ },
2
8
  {
3
9
  "version": "0.1.69",
4
10
  "date": "2026-10-20",
@@ -1,34 +1,16 @@
1
1
  import path from 'node:path';
2
- import { randomUUID, sign } from 'node:crypto';
3
- import { canonical, sha256 } from './local-integrity.mjs';
2
+ import { sign } from 'node:crypto';
3
+ import { canonical } from './local-integrity.mjs';
4
4
  import { withStateLock } from './local-state-lock.mjs';
5
- import { atomicMutationJson14, readMutationJson14 } from './local-mutation-files.mjs';
6
5
  import { bootstrapCloudMutationAuthority } from './cloud-mutation-authority.mjs';
7
6
  import { contextInput, contextFail, retainedContexts, originalContextDigest, contextChallenge } from './scope-context-wire.mjs';
8
7
  import { scopeContextLocation, effectiveScopeContexts, retainContextAuthority,
9
- readContextJournal, saveContextJournal } from './scope-context-state.mjs';
8
+ saveContextJournal } from './scope-context-state.mjs';
10
9
  import { contextRemote } from './scope-context-remote.mjs';
11
- import { inspectContextEvidence, observeScopeContext } from './scope-context-observer.mjs';
10
+ import { inspectContextEvidence, observeScopeContext, prepareScopeContext } from './scope-context-observer.mjs';
11
+ import { contextJournal, renewContextChallenge } from './scope-context-journal.mjs';
12
12
 
13
13
  const schemaVersion = 'entrorove.scope-context-connection/1.0';
14
- async function archive(location, state) {
15
- if (!state.attemptId) return;
16
- const file = path.join(location.directory, `${state.attemptId}-${sha256(canonical(state))}.json`);
17
- try { if (canonical(await readMutationJson14(file)) !== canonical(state)) contextFail('Context attempt history changed'); }
18
- catch (error) { if (error.code !== 'ENOENT') throw error; await atomicMutationJson14(file, state, { privateFrom: location.stateRoot }); }
19
- }
20
- async function journal(location, input) {
21
- let state = await readContextJournal(location, input);
22
- if (state.previous || input.action === 'status' && state.completed
23
- || input.action === 'apply' && state.completed && !state.applied || state.rejection && !state.pending) {
24
- await archive(location, state.previous ?? state); state = {};
25
- }
26
- if (!state.attemptId) {
27
- state = { schemaVersion: 'entrorove.scope-context-journal/1.0', localBindingDigest: location.localBindingDigest,
28
- input, attemptId: randomUUID() }; await saveContextJournal(location, state);
29
- }
30
- return state;
31
- }
32
14
  function proposalFor(status, input, location) {
33
15
  const proposal = status.proposal;
34
16
  if (!proposal || proposal.proposalId !== input.proposalId || proposal.proposalDigest !== input.proposalDigest
@@ -73,7 +55,7 @@ async function review(location, state, dependencies) {
73
55
  }
74
56
  async function applied(location, state) {
75
57
  const effective = state.applied.contextAuthority;
76
- const challenge = contextChallenge(state.challenged.challenge, location, state.proposal, { allowExpired: true });
58
+ const challenge = contextChallenge((state.signingChallenge ?? state.challenged).challenge, location, state.proposal, { allowExpired: true });
77
59
  if (state.applied.grantsExecutionAuthority !== false || !effective?.record
78
60
  || canonical(effective.record.attestation) !== canonical(state.attestation)
79
61
  || ['proposalId', 'proposalDigest', 'ownerId', 'taskDigest', 'chainDigest'].some(field => effective.record[field] !== challenge[field])
@@ -94,16 +76,18 @@ async function apply(location, state, dependencies) {
94
76
  state.proposal = proposal; await saveContextJournal(location, state);
95
77
  if (!state.challenged) await contextRemote('challenged', 'scope-runner-context-challenge', { flowId: location.binding.flowId,
96
78
  proposalId: proposal.proposalId, proposalDigest: proposal.proposalDigest, attemptId: state.attemptId }, state, location, dependencies);
97
- contextChallenge(state.challenged.challenge, location, proposal);
98
79
  if (!state.attestation) {
99
- state.attestation = await observeScopeContext(location, state, proposal); await saveContextJournal(location, state);
80
+ const prepared = await prepareScopeContext(location, state, proposal);
81
+ state.attestation = await observeScopeContext(location, state, proposal, prepared,
82
+ () => renewContextChallenge(location, state, proposal, dependencies));
83
+ await saveContextJournal(location, state);
100
84
  }
101
85
  await contextRemote('applied', 'scope-runner-context-apply', { flowId: location.binding.flowId,
102
86
  proposalId: proposal.proposalId, proposalDigest: proposal.proposalDigest, attestation: state.attestation }, state, location, dependencies);
103
87
  return applied(location, state);
104
88
  }
105
89
  async function run(location, input, dependencies) {
106
- const state = await journal(location, input);
90
+ const state = await contextJournal(location, input);
107
91
  if (state.pending) await contextRemote(state.pending.stage, state.pending.operation, state.pending.input, state, location, dependencies);
108
92
  try {
109
93
  if (input.action === 'review') return await review(location, state, dependencies);
@@ -0,0 +1,44 @@
1
+ import path from 'node:path';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { canonical, sha256 } from './local-integrity.mjs';
4
+ import { atomicMutationJson14, readMutationJson14 } from './local-mutation-files.mjs';
5
+ import { contextFail } from './scope-context-wire.mjs';
6
+ import { readContextJournal, saveContextJournal } from './scope-context-state.mjs';
7
+ import { contextRemote } from './scope-context-remote.mjs';
8
+
9
+ export async function archiveContextAttempt(location, state) {
10
+ if (!state.attemptId) return;
11
+ const file = path.join(location.directory, `${state.attemptId}-${sha256(canonical(state))}.json`);
12
+ try { if (canonical(await readMutationJson14(file)) !== canonical(state)) contextFail('Context attempt history changed'); }
13
+ catch (error) { if (error.code !== 'ENOENT') throw error; await atomicMutationJson14(file, state, { privateFrom: location.stateRoot }); }
14
+ }
15
+ function reusableSnapshot(state, input) {
16
+ return input.action === 'apply' && state.snapshot && state.challenged && !state.pending && !state.completed && !state.applied
17
+ && state.rejection && !state.rejection.requestId && !state.rejection.output;
18
+ }
19
+ export async function contextJournal(location, input) {
20
+ let state = await readContextJournal(location, input);
21
+ if (!state.previous && reusableSnapshot(state, input)) {
22
+ await archiveContextAttempt(location, state);
23
+ for (const field of ['rejection', 'checked', 'attestation', 'signingChallenge', 'signingAttemptId']) delete state[field];
24
+ await saveContextJournal(location, state);
25
+ } else if (state.previous || !state.pending && !state.applied && (state.rejection
26
+ || input.action === 'status' && state.completed || input.action === 'apply' && state.completed)) {
27
+ await archiveContextAttempt(location, state.previous ?? state); state = {};
28
+ }
29
+ if (!state.attemptId) {
30
+ state = { schemaVersion: 'entrorove.scope-context-journal/1.0', localBindingDigest: location.localBindingDigest,
31
+ input, attemptId: randomUUID() }; await saveContextJournal(location, state);
32
+ }
33
+ return state;
34
+ }
35
+ export async function renewContextChallenge(location, state, proposal, dependencies) {
36
+ if (state.pending) contextFail('Recover the original pending request before any challenge renewal');
37
+ await archiveContextAttempt(location, state);
38
+ delete state.signingChallenge;
39
+ state.signingAttemptId = randomUUID(); await saveContextJournal(location, state);
40
+ const output = await contextRemote('signingChallenge', 'scope-runner-context-challenge', { flowId: location.binding.flowId,
41
+ proposalId: proposal.proposalId, proposalDigest: proposal.proposalDigest, attemptId: state.signingAttemptId },
42
+ state, location, dependencies);
43
+ return output.challenge;
44
+ }
@@ -25,17 +25,21 @@ export async function inspectContextEvidence(location, contexts, evidencePaths)
25
25
  }
26
26
  return evidence;
27
27
  }
28
- export async function observeScopeContext(location, state, proposal) {
29
- const challenge = contextChallenge(state.challenged.challenge, location, proposal);
30
- const evidence = await inspectContextEvidence(location, challenge.importContexts, challenge.evidence.map(file => file.path));
31
- if (canonical(evidence) !== canonical(challenge.evidence)) contextFail('Actual build configuration or loader evidence changed since review');
32
- if (!state.snapshotId) { state.snapshotId = `context-${randomUUID()}`; await saveContextJournal(location, state); }
33
- const input = { repositoryRoot: location.root, home: location.home, flowId: challenge.flowId, stepId: challenge.stepId,
28
+ function observationInput(location, state, proposal, challenge) {
29
+ return { repositoryRoot: location.root, home: location.home, flowId: challenge.flowId, stepId: challenge.stepId,
34
30
  snapshotId: state.snapshotId, paths: [...location.binding.targetPaths], importContexts: challenge.importContexts,
35
31
  contextRevisionDigest: proposal.proposalDigest, contextEvidence: challenge.evidence,
36
32
  signerConfigPath: location.signerConfigPath, ownerId: challenge.ownerId,
37
33
  task: challenge.task, taskDigest: challenge.taskDigest, chainDigest: challenge.chainDigest,
38
34
  repositoryId: challenge.repositoryId, challenge: challenge.challenge };
35
+ }
36
+ export async function prepareScopeContext(location, state, proposal) {
37
+ // An expired server challenge can still identify read-only preparation; it cannot sign or apply.
38
+ const challenge = contextChallenge(state.challenged.challenge, location, proposal, { allowExpired: true });
39
+ const evidence = await inspectContextEvidence(location, challenge.importContexts, challenge.evidence.map(file => file.path));
40
+ if (canonical(evidence) !== canonical(challenge.evidence)) contextFail('Actual build configuration or loader evidence changed since review');
41
+ if (!state.snapshotId) { state.snapshotId = `context-${randomUUID()}`; await saveContextJournal(location, state); }
42
+ const input = observationInput(location, state, proposal, challenge);
39
43
  if (!state.snapshot) {
40
44
  if (state.snapshotStarted) contextFail('Interrupted context snapshot must be inspected before another observation; preserve its files');
41
45
  state.snapshotStarted = true; await saveContextJournal(location, state);
@@ -48,13 +52,34 @@ export async function observeScopeContext(location, state, proposal) {
48
52
  const snapshot = await readScopeSnapshot(input, location.root);
49
53
  const facts = await collectScopeFacts(location.root, input.paths, undefined, snapshot.known, input);
50
54
  facts.snapshotEvidence = await verifyFreshScopeSnapshot(input, location.root, facts);
55
+ return { input, facts, evidence };
56
+ }
57
+ function sameObservation(candidate, input, challenge) {
58
+ const withoutNonce = value => Object.fromEntries(Object.entries(value).filter(([field]) => field !== 'challenge'));
59
+ if (canonical(withoutNonce(candidate.input)) !== canonical(withoutNonce(input))
60
+ || canonical(candidate.evidence) !== canonical(challenge.evidence)) contextFail('Fresh challenge replaced original preparation identity');
61
+ }
62
+ export async function observeScopeContext(location, state, proposal, prepared, renewChallenge) {
63
+ const candidate = prepared ?? await prepareScopeContext(location, state, proposal);
64
+ let challenge = contextChallenge((state.signingChallenge ?? state.challenged).challenge, location, proposal,
65
+ { allowExpired: typeof renewChallenge === 'function' });
66
+ const input = observationInput(location, state, proposal, challenge);
67
+ sameObservation(candidate, input, challenge);
68
+ const beforeSign = renewChallenge && (async () => {
69
+ challenge = contextChallenge(await renewChallenge(), location, proposal);
70
+ sameObservation(candidate, observationInput(location, state, proposal, challenge), challenge);
71
+ return { challenge: challenge.challenge, maximumExpiresAt: Date.parse(challenge.expiresAt) };
72
+ });
73
+ const facts = candidate.facts;
51
74
  const observed = await attestScopeFacts(input, { repositoryRoot: location.root, facts,
52
- snapshotDigest: facts.snapshotEvidence.snapshotDigest }, { maximumExpiresAt: Date.parse(challenge.expiresAt) });
53
- if (!observed.attestation || canonical(observed.attestation.importContexts) !== canonical(challenge.importContexts)
75
+ snapshotDigest: facts.snapshotEvidence.snapshotDigest }, beforeSign ? { beforeSign }
76
+ : { maximumExpiresAt: Date.parse(challenge.expiresAt) });
77
+ if (!observed.attestation || observed.attestation.challenge !== challenge.challenge
78
+ || canonical(observed.attestation.importContexts) !== canonical(challenge.importContexts)
54
79
  || observed.attestation.contextRevisionDigest !== proposal.proposalDigest
55
80
  || Date.parse(observed.attestation.expiresAt) > Date.parse(challenge.expiresAt)) contextFail('Context candidate requires its actual purpose-bound original S proof');
56
81
  const after = await inspectContextEvidence(location, challenge.importContexts, challenge.evidence.map(file => file.path));
57
- if (canonical(after) !== canonical(evidence)) contextFail('Build evidence changed while observing its actual imports');
82
+ if (canonical(after) !== canonical(candidate.evidence)) contextFail('Build evidence changed while observing its actual imports');
58
83
  const core = { schemaVersion: 'entrorove.scope-context-attestation/1.0', challengeId: challenge.challengeId,
59
84
  proposalDigest: proposal.proposalDigest, scopeAttestation: observed.attestation };
60
85
  return { ...core, signature: sign(null, Buffer.from(canonical(core)), location.key).toString('base64url') };
@@ -1,6 +1,6 @@
1
1
  export const SCOPE_CONTEXT_SOURCE_FILES = [
2
2
  'scope-context-client.mjs', 'scope-context-observer.mjs', 'scope-context-observation.mjs', 'scope-context-remote.mjs',
3
- 'scope-context-state.mjs', 'scope-context-wire.mjs', 'scope-context-release.mjs',
3
+ 'scope-context-state.mjs', 'scope-context-wire.mjs', 'scope-context-journal.mjs', 'scope-context-release.mjs',
4
4
  ]
5
5
  export const scopeContextReleaseNote = {
6
6
  zh: '新增首次范围门禁前的构建上下文纠正:原账号审核真实配置和构建证据,原 S 签名完整路径快照,通过服务挑战追加上下文权威记录;保留原 flow、绑定、旧上下文和失败历史,不授予写入或完成权限。',