@entrorove/skill 0.1.68 → 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.
Files changed (48) hide show
  1. package/CAPABILITY-MAP.md +2 -0
  2. package/CHANGELOG.md +10 -0
  3. package/IDE-EXECUTION.md +16 -2
  4. package/SKILL.md +3 -2
  5. package/cli.mjs +1 -1
  6. package/cloud-locale.d.mts +7 -0
  7. package/cloud-locale.mjs +59 -0
  8. package/cloud-task-client.mjs +21 -13
  9. package/cloud-watch-client.mjs +11 -3
  10. package/connected-cli.mjs +2 -0
  11. package/design-lint.mjs +20 -2
  12. package/ide-command.mjs +1 -1
  13. package/ide-hook.mjs +4 -4
  14. package/ide-presentation.mjs +15 -4
  15. package/local-archguard-components.mjs +31 -4
  16. package/local-contracts.mjs +5 -2
  17. package/local-delegated-modify-analysis.mjs +16 -7
  18. package/local-discipline-gate.mjs +2 -0
  19. package/local-discipline-snapshot.mjs +8 -2
  20. package/local-mutation-snapshot.mjs +2 -2
  21. package/local-preserved-mutation-protection.mjs +2 -0
  22. package/local-scope-files.mjs +14 -2
  23. package/local-scope-read-set.mjs +66 -0
  24. package/local-scope-repository.mjs +97 -13
  25. package/local-scope-signature.mjs +52 -6
  26. package/local-scope-uni-build.mjs +135 -0
  27. package/local-scope-uni-conditional.mjs +54 -0
  28. package/local-scope-uni-directives.mjs +96 -0
  29. package/local-scope-uni-expressions.mjs +101 -0
  30. package/local-scope-workspaces.mjs +14 -2
  31. package/manifest.json +1 -1
  32. package/mutation-client.mjs +8 -5
  33. package/package.json +17 -2
  34. package/release-state.json +46 -31
  35. package/release.mjs +4 -3
  36. package/releases.json +12 -0
  37. package/remote-client.mjs +1 -0
  38. package/scope-amendment-observer.mjs +3 -1
  39. package/scope-amendment-state.mjs +3 -1
  40. package/scope-context-client.mjs +118 -0
  41. package/scope-context-journal.mjs +44 -0
  42. package/scope-context-observation.mjs +42 -0
  43. package/scope-context-observer.mjs +86 -0
  44. package/scope-context-release.mjs +8 -0
  45. package/scope-context-remote.mjs +60 -0
  46. package/scope-context-state.mjs +88 -0
  47. package/scope-context-wire.mjs +84 -0
  48. package/scope-runner-client.mjs +9 -5
@@ -0,0 +1,135 @@
1
+ import path from 'node:path';
2
+ import ts from 'typescript';
3
+ import { uniConditionalError } from './local-scope-uni-expressions.mjs';
4
+
5
+ const controls = /\bUNI_(?:CUSTOM_DEFINE|SCRIPT_(?:DEFINE|ENV)|COMPILER_VERSION|APP_X(?:_DOM2)?|UTS_PLATFORM)\b/;
6
+ const controlKeys = ['UNI_CUSTOM_DEFINE', 'UNI_SCRIPT_DEFINE', 'UNI_SCRIPT_ENV', 'UNI_COMPILER_VERSION', 'UNI_APP_X', 'UNI_APP_X_DOM2', 'UNI_UTS_PLATFORM'];
7
+ const configNames = ['vite.config.js', 'vite.config.mjs', 'vite.config.cjs', 'vite.config.ts',
8
+ 'vite.config.mts', 'vite.config.cts', 'vue.config.js', 'vue.config.cjs'];
9
+ function staticConfiguration(source, filename, fail) {
10
+ if (controls.test(source)) throw fail('Custom conditional macro or compiler controls require explicit scanner support');
11
+ const tree = ts.createSourceFile(filename, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS), imports = new Map();
12
+ if (tree.parseDiagnostics.length) throw fail('Conditional build configuration has invalid syntax');
13
+ let exported = false, nodes = 0;
14
+ const value = (node, depth = 0) => {
15
+ if (++nodes > 2048 || depth > 24) throw fail('Conditional build configuration exceeds its static inspection bound');
16
+ if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node)) return value(node.expression, depth + 1);
17
+ if (ts.isStringLiteral(node) || ts.isNumericLiteral(node)
18
+ || [ts.SyntaxKind.TrueKeyword, ts.SyntaxKind.FalseKeyword, ts.SyntaxKind.NullKeyword].includes(node.kind)) return;
19
+ if (ts.isArrayLiteralExpression(node)) { for (const item of node.elements) value(item, depth + 1); return; }
20
+ if (ts.isObjectLiteralExpression(node)) {
21
+ for (const property of node.properties) {
22
+ if (!ts.isPropertyAssignment(property) || ts.isComputedPropertyName(property.name)) throw fail('Dynamic build properties cannot establish default conditional macros');
23
+ value(property.initializer, depth + 1);
24
+ }
25
+ return;
26
+ }
27
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
28
+ const operation = imports.get(node.expression.text);
29
+ if (operation === 'uni' && node.arguments.length === 0) return;
30
+ if (operation === 'defineConfig' && node.arguments.length === 1) return value(node.arguments[0], depth + 1);
31
+ }
32
+ throw fail('Dynamic build configuration cannot establish default conditional macros without execution');
33
+ };
34
+ for (const statement of tree.statements) {
35
+ if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
36
+ const module = statement.moduleSpecifier.text, clause = statement.importClause;
37
+ if (module === '@dcloudio/vite-plugin-uni' && clause?.name && !clause.namedBindings) imports.set(clause.name.text, 'uni');
38
+ else if (module === 'vite' && !clause?.name && clause?.namedBindings && ts.isNamedImports(clause.namedBindings)
39
+ && clause.namedBindings.elements.every(element => (element.propertyName ?? element.name).text === 'defineConfig')) {
40
+ for (const element of clause.namedBindings.elements) imports.set(element.name.text, 'defineConfig');
41
+ } else throw fail('Custom build imports cannot establish default conditional macros without execution');
42
+ } else if (ts.isExportAssignment(statement) && !exported && !statement.isExportEquals) { exported = true; value(statement.expression); }
43
+ else if (ts.isExpressionStatement(statement) && !exported && ts.isBinaryExpression(statement.expression)
44
+ && statement.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken
45
+ && statement.expression.left.getText(tree) === 'module.exports') { exported = true; value(statement.expression.right); }
46
+ else if (!ts.isEmptyStatement(statement)) throw fail('Custom build statements cannot establish default conditional macros without execution');
47
+ }
48
+ if (!exported) throw fail('Conditional build configuration requires a statically inspectable export');
49
+ }
50
+ function packageConfiguration(source, fail) {
51
+ let manifest;
52
+ try { manifest = JSON.parse(source); } catch { throw fail('Conditional build package manifest is invalid JSON'); }
53
+ const scripts = manifest?.['uni-app']?.scripts;
54
+ if (scripts !== undefined) {
55
+ if (!scripts || typeof scripts !== 'object' || Array.isArray(scripts)) throw fail('Custom uni-app build scripts are not statically inspectable');
56
+ for (const configuration of Object.values(scripts)) {
57
+ if (!configuration || typeof configuration !== 'object' || Array.isArray(configuration)) throw fail('Custom uni-app build entry is not statically inspectable');
58
+ if (configuration.define !== undefined && (!configuration.define || typeof configuration.define !== 'object'
59
+ || Array.isArray(configuration.define) || Object.keys(configuration.define).length)) {
60
+ throw fail('Custom uni-app defines, including builtin macro overrides, require explicit scanner support');
61
+ }
62
+ if (controls.test(JSON.stringify(configuration))) throw fail('Custom conditional build environment requires explicit scanner support');
63
+ }
64
+ }
65
+ for (const [name, command] of Object.entries(manifest?.scripts ?? {})) {
66
+ if (typeof command !== 'string' || controls.test(command)) throw fail('Custom conditional build command requires explicit scanner support');
67
+ if (/^(?:dev|build)(?::|$)/.test(name) && (!/^(?:cross-env\s+NODE_ENV=(?:development|production|test)\s+)?(?:uni|vite|vue-cli-service\s+uni-(?:build|serve))(?:\s+[A-Za-z0-9_./:@=-]+)*$/.test(command)
68
+ || /(?:^|\s)(?:--config(?:[=\s]|$)|-c(?:\s|$)|--define(?:[=\s]|$))/.test(command))) {
69
+ throw fail('Custom conditional build command requires explicit scanner support');
70
+ }
71
+ }
72
+ }
73
+ function parentDirectories(relative, output, fail) {
74
+ let directory = path.posix.dirname(relative);
75
+ while (true) {
76
+ output.add(directory);
77
+ if (output.size > 128) throw fail('Conditional build roots exceed their 128-directory inspection bound');
78
+ if (directory === '.') return;
79
+ directory = path.posix.dirname(directory);
80
+ }
81
+ }
82
+ function assertProcessControls(filename) {
83
+ const present = controlKeys.filter(key => Object.hasOwn(process.env, key) || Object.hasOwn(process, key));
84
+ if (present.length) throw uniConditionalError(filename, 0, `Conditional compilation override controls are unsupported: ${present.join(', ')}`);
85
+ }
86
+ export function createUniBuildInspection(read, inspectDirectory) {
87
+ const inspected = new Map(), directoriesInspected = new Set(); let candidates = 0, bytes = 0, directoryEntries = 0;
88
+ const inspect = async relative => {
89
+ if (inspected.has(relative)) return inspected.get(relative);
90
+ if (++candidates > 2048) throw uniConditionalError(relative, 0, 'Conditional build lookup exceeds its 2048-path bound');
91
+ let item;
92
+ try { item = await read(relative); } catch (error) { if (error.code === 'ENOENT') { inspected.set(relative, null); return null; } throw error; }
93
+ const source = Buffer.from(item.contentBase64, 'base64').toString('utf8'), fail = reason => uniConditionalError(relative, 0, reason);
94
+ bytes += Buffer.byteLength(source);
95
+ if (Buffer.byteLength(source) > 1_048_576 || bytes > 8_388_608) throw fail('Conditional build metadata exceeds its bounded inspection size');
96
+ if (relative.endsWith('package.json')) packageConfiguration(source, fail); else staticConfiguration(source, relative, fail);
97
+ const observed = { path: item.path, sha256: item.sha256 }; inspected.set(relative, observed); return observed;
98
+ };
99
+ const inspectSource = async (sourcePath, configPath) => {
100
+ const directories = new Set(), fail = reason => uniConditionalError(sourcePath, 0, reason);
101
+ assertProcessControls(sourcePath);
102
+ parentDirectories(sourcePath, directories, fail); if (configPath) parentDirectories(configPath, directories, fail);
103
+ const observations = new Map();
104
+ for (const directory of directories) {
105
+ if (!directoriesInspected.has(directory)) {
106
+ if (typeof inspectDirectory !== 'function') throw fail('Conditional build environment absence requires actual bounded directory inspection');
107
+ const listing = await inspectDirectory(directory, Math.min(8192, 32768 - directoryEntries));
108
+ directoryEntries += listing.entries;
109
+ if (listing.environmentFiles.length) throw fail('Conditional sources with ancestor environment files require explicit scanner support; environment values were not read');
110
+ directoriesInspected.add(directory);
111
+ }
112
+ for (const name of ['package.json', ...configNames]) {
113
+ const relative = directory === '.' ? name : `${directory}/${name}`, observed = await inspect(relative);
114
+ if (observed) observations.set(relative, observed);
115
+ }
116
+ }
117
+ return [...observations.values()];
118
+ };
119
+ inspectSource.verify = async () => {
120
+ if (!directoriesInspected.size) return;
121
+ assertProcessControls('conditional-build'); let entries = 0;
122
+ for (const directory of directoriesInspected) {
123
+ const listing = await inspectDirectory(directory, Math.min(8192, 32768 - entries)); entries += listing.entries;
124
+ if (listing.environmentFiles.length) throw Object.assign(uniConditionalError(directory, 0,
125
+ 'Conditional build environment appeared after its actual inspection; environment values were not read'), { code: 'ENTROROVE_SCOPE_CHANGED' });
126
+ }
127
+ for (const [relative, expected] of inspected) {
128
+ let actual;
129
+ try { actual = await read(relative); } catch (error) { if (error.code !== 'ENOENT') throw error; }
130
+ if ((actual?.sha256 ?? null) !== (expected?.sha256 ?? null)) throw Object.assign(
131
+ uniConditionalError(relative, 0, 'Conditional build metadata changed during inspection'), { code: 'ENTROROVE_SCOPE_CHANGED' });
132
+ }
133
+ };
134
+ return inspectSource;
135
+ }
@@ -0,0 +1,54 @@
1
+ import { uniDirectives } from './local-scope-uni-directives.mjs';
2
+ import { uniCondition, uniConditionContexts, uniConditionalError } from './local-scope-uni-expressions.mjs';
3
+
4
+ const blank = source => source.replace(/[^\r\n]/g, ' ');
5
+ function conditionalSegments(source, directives, filename) {
6
+ const stack = [], segments = [], conditions = []; let cursor = 0, conditionTokens = 0;
7
+ for (const item of directives) {
8
+ segments.push({ start: cursor, end: item.start, guards: stack.map(guard => ({ ...guard })) });
9
+ segments.push({ start: item.start, end: item.end, directive: true }); cursor = item.end;
10
+ const fail = reason => uniConditionalError(filename, item.start, reason, source);
11
+ if (['ifdef', 'ifndef', 'if'].includes(item.kind)) {
12
+ if (stack.length >= 32) throw fail('Conditional compilation exceeds its 32-level nesting bound');
13
+ const condition = uniCondition(item.expression, fail), index = conditions.length;
14
+ if ((conditionTokens += condition.tokens) > 4096) throw fail('Conditional expressions exceed their 4096-token aggregate inspection bound');
15
+ conditions.push(condition); stack.push({ index, negated: item.kind === 'ifndef', syntax: item.syntax, alternative: false });
16
+ } else {
17
+ const active = stack.at(-1);
18
+ if (!active || active.syntax !== item.syntax) throw fail('Conditional directive has no matching opening in the same source language');
19
+ if (item.kind === 'else') {
20
+ if (active.alternative) throw fail('Conditional block has more than one alternative');
21
+ active.negated = !active.negated; active.alternative = true;
22
+ } else stack.pop();
23
+ }
24
+ }
25
+ if (stack.length) throw uniConditionalError(filename, directives.at(-1).start, 'Conditional block is missing its matching endif', source);
26
+ segments.push({ start: cursor, end: source.length, guards: [] });
27
+ return { conditions, segments };
28
+ }
29
+ const activeSegment = (segment, key) => !segment.directive && segment.guards.every(guard => (key[guard.index] === '1') !== guard.negated);
30
+ export function uniConditionalVariants(source, filename) {
31
+ const fail = reason => uniConditionalError(filename, 0, reason, source);
32
+ if (typeof source !== 'string') throw fail('Conditional inspection requires actual source text');
33
+ const unchanged = () => ({ directives: [], variants: [{ source, key: '', label: 'unconditional' }] });
34
+ if (!/(?:<!--|\/\/|\/\*)[ \t]*#/i.test(source)) return unchanged();
35
+ if (Buffer.byteLength(source) > 2_097_152) throw fail('Conditional source exceeds its 2 MiB inspection bound');
36
+ try {
37
+ const directives = uniDirectives(source, filename);
38
+ if (!directives.length) return unchanged();
39
+ const { conditions, segments } = conditionalSegments(source, directives, filename), contexts = uniConditionContexts(conditions, fail);
40
+ if (Buffer.byteLength(source) * contexts.length > 33_554_432) throw fail('Conditional source variants exceed their 32 MiB inspection bound');
41
+ for (const segment of segments) if (!segment.directive && source.slice(segment.start, segment.end).trim()
42
+ && !contexts.some(context => activeSegment(segment, context.key))) {
43
+ throw uniConditionalError(filename, segment.start, 'Conditional branch has no proven platform context; custom or contradictory build definitions require explicit support', source);
44
+ }
45
+ const variants = contexts.map(context => ({ key: context.key, label: context.label,
46
+ source: segments.map(segment => {
47
+ const text = source.slice(segment.start, segment.end); return activeSegment(segment, context.key) ? text : blank(text);
48
+ }).join('') }));
49
+ return { directives: directives.map(({ kind, expression, syntax }) => ({ kind, expression, syntax })), variants };
50
+ } catch (error) {
51
+ if (error instanceof RangeError) throw Object.assign(fail('Conditional syntax exceeds its parser or traversal bound'), { code: 'ENTROROVE_SCOPE_LIMIT' });
52
+ throw error;
53
+ }
54
+ }
@@ -0,0 +1,96 @@
1
+ import ts from 'typescript';
2
+ import { uniConditionalError } from './local-scope-uni-expressions.mjs';
3
+
4
+ function directive(source, start, end, text, syntax, filename, output) {
5
+ const match = /^\s*#([a-z]+)\b([\s\S]*)$/i.exec(text);
6
+ if (!match) return;
7
+ const kind = match[1].toLowerCase(), expression = match[2].trim();
8
+ if (!['ifdef', 'ifndef', 'if', 'else', 'endif', 'elif', 'elseif', 'exec', 'include', 'define', 'exclude', 'endexclude'].includes(kind)) return;
9
+ if (!['ifdef', 'ifndef', 'if', 'else', 'endif'].includes(kind) || /[\r\n]/.test(expression)
10
+ || ['else', 'endif'].includes(kind) && expression) {
11
+ throw uniConditionalError(filename, start, 'Conditional directive syntax is unsupported', source);
12
+ }
13
+ if (output.length >= 256) throw uniConditionalError(filename, start, 'Conditional directives exceed their 256-entry bound', source);
14
+ output.push({ start, end, kind, expression, syntax });
15
+ }
16
+ function literalRanges(content, kind) {
17
+ const parsed = ts.createSourceFile('uni-script.ts', content, ts.ScriptTarget.Latest, true, kind);
18
+ const pending = [parsed], ranges = []; let nodes = 0;
19
+ while (pending.length) {
20
+ const node = pending.pop();
21
+ if (++nodes > 200_000) throw new RangeError('Conditional lexical inspection exceeds its node bound');
22
+ if (ts.isStringLiteralLike(node) || ts.isRegularExpressionLiteral(node)
23
+ || [ts.SyntaxKind.TemplateHead, ts.SyntaxKind.TemplateMiddle, ts.SyntaxKind.TemplateTail].includes(node.kind)) {
24
+ ranges.push({ start: node.getStart(parsed), end: node.end });
25
+ } else ts.forEachChild(node, child => { pending.push(child); });
26
+ }
27
+ return ranges.sort((a, b) => a.start - b.start);
28
+ }
29
+ function scriptComments(source, start, end, filename, output, kind) {
30
+ const content = source.slice(start, end), literals = literalRanges(content, kind); let literal = 0;
31
+ for (let index = 0; index < content.length;) {
32
+ while (literals[literal]?.end <= index) literal++;
33
+ if (literals[literal]?.start <= index) { index = literals[literal].end; continue; }
34
+ if (content.startsWith('//', index)) {
35
+ let next = index + 2; while (next < content.length && !['\r', '\n'].includes(content[next])) next++;
36
+ directive(source, start + index, start + next, content.slice(index + 2, next), 'js', filename, output); index = next;
37
+ } else if (content.startsWith('/*', index)) {
38
+ const finish = content.indexOf('*/', index + 2), next = finish < 0 ? content.length : finish + 2;
39
+ directive(source, start + index, start + next, content.slice(index + 2, finish < 0 ? next : finish), 'js', filename, output); index = next;
40
+ } else index++;
41
+ }
42
+ }
43
+ function quoteEnd(source, index, quote, end = source.length) {
44
+ for (let cursor = index + 1; cursor < end; cursor++) {
45
+ if (source[cursor] === '\\') cursor++;
46
+ else if (source[cursor] === quote) return cursor + 1;
47
+ }
48
+ return end;
49
+ }
50
+ function styleComments(source, start, end, filename, output) {
51
+ for (let cursor = start; cursor < end;) {
52
+ if (['"', "'"].includes(source[cursor])) cursor = quoteEnd(source, cursor, source[cursor], end);
53
+ else if (source.startsWith('/*', cursor)) {
54
+ const finish = source.indexOf('*/', cursor + 2), next = finish < 0 || finish >= end ? end : finish + 2;
55
+ directive(source, cursor, next, source.slice(cursor + 2, next - 2), 'css', filename, output); cursor = next;
56
+ } else cursor++;
57
+ }
58
+ }
59
+ function tagEnd(source, start) {
60
+ for (let cursor = start; cursor < source.length; cursor++) {
61
+ if (['"', "'"].includes(source[cursor])) cursor = quoteEnd(source, cursor, source[cursor]) - 1;
62
+ else if (source[cursor] === '>') return cursor + 1;
63
+ }
64
+ return source.length;
65
+ }
66
+ function interpolationEnd(source, start) {
67
+ for (let cursor = start + 2; cursor < source.length; cursor++) {
68
+ if (['"', "'", '`'].includes(source[cursor])) cursor = quoteEnd(source, cursor, source[cursor]) - 1;
69
+ else if (source.startsWith('}}', cursor)) return cursor + 2;
70
+ }
71
+ return source.length;
72
+ }
73
+ export function uniDirectives(source, filename) {
74
+ const output = []; let inTag = false;
75
+ for (let cursor = 0; cursor < source.length;) {
76
+ if (source.startsWith('<!--', cursor)) {
77
+ const finish = source.indexOf('-->', cursor + 4), next = finish < 0 ? source.length : finish + 3;
78
+ directive(source, cursor, next, source.slice(cursor + 4, finish < 0 ? next : finish), 'html', filename, output); cursor = next;
79
+ } else if (inTag && ['"', "'"].includes(source[cursor])) cursor = quoteEnd(source, cursor, source[cursor]);
80
+ else if (source.startsWith('{{', cursor)) cursor = interpolationEnd(source, cursor);
81
+ else {
82
+ const block = /^<(script|style)\b/i.exec(source.slice(cursor, cursor + 16));
83
+ if (block) {
84
+ const start = tagEnd(source, cursor), closing = new RegExp(`</${block[1]}\\s*>`, 'ig'); closing.lastIndex = start;
85
+ const match = closing.exec(source), end = match?.index ?? source.length;
86
+ if (block[1].toLowerCase() === 'script') {
87
+ const header = source.slice(cursor, start), language = /\blang\s*=\s*['"]([^'"]+)['"]/i.exec(header)?.[1];
88
+ scriptComments(source, start, end, filename, output, ['ts', 'typescript'].includes(language) ? ts.ScriptKind.TS : ts.ScriptKind.TSX);
89
+ }
90
+ else styleComments(source, start, end, filename, output);
91
+ cursor = match ? closing.lastIndex : source.length; inTag = false;
92
+ } else { if (source[cursor] === '<') inTag = true; else if (source[cursor] === '>') inTag = false; cursor++; }
93
+ }
94
+ }
95
+ return output;
96
+ }
@@ -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.68",
4
+ "version": "0.1.70",
5
5
  "endpoint": "https://entrorove.com",
6
6
  "requestSchemaVersion": "entrorove.skill.request/2.0",
7
7
  "supportedRequestSchemaVersions": [
@@ -20,6 +20,7 @@ import { scopeAmendmentRefreshAction } from './scope-amendment-reference.mjs';
20
20
  import { readMutationConnection as readConnection } from './mutation-connection-state.mjs';
21
21
  import { readMutationEnvironmentObservation } from './local-mutation-environment-observation.mjs';
22
22
  import { refreshExpiredMutationChallenge } from './mutation-challenge-refresh.mjs';
23
+ import { scopeContextObservation, scopeContextSnapshotInput } from './scope-context-observation.mjs';
23
24
 
24
25
  const fields = ['taskId', 'flowId', 'stepId', 'leaseId', 'generation', 'runnerId', 'paths'];
25
26
  const fail = message => mutationFail14('ENTROROVE_MUTATION_CONNECTION_BINDING', message);
@@ -110,14 +111,14 @@ function passBinding(pass, challenge, attestation, options) {
110
111
  .some(field => canonical(pass[field]) !== canonical(challenge[field]))
111
112
  || canonical(pass.writeDelegation) !== canonical(challenge.writeDelegation)) fail('Mutation pass differs from its original signed challenge or actual M snapshot');
112
113
  }
113
- async function snapshot(input, challenge, state, location) {
114
+ async function snapshot(input, challenge, state, location, contextAuthority) {
114
115
  if (!state.planned) {
115
116
  if (state.planStarted) {
116
117
  const recovered = await snapshotVerify14(input);
117
118
  state.planned = { snapshotId: recovered.receipt.snapshotId, snapshotDigest: recovered.receipt.snapshotDigest };
118
119
  } else {
119
120
  state.planStarted = true; await save(location, state);
120
- try { state.planned = await snapshotPlan14({ ...input, paths: challenge.paths, contractDigest: challenge.scopeContractDigest }); }
121
+ try { state.planned = await snapshotPlan14({ ...input, paths: challenge.paths, contractDigest: challenge.scopeContractDigest }, { contextAuthority }); }
121
122
  catch (error) { delete state.planStarted; await save(location, state); throw error; }
122
123
  }
123
124
  await save(location, state);
@@ -149,7 +150,7 @@ async function authorizeCachedConnection(identity, pass, dependencies) {
149
150
  }
150
151
  return status;
151
152
  }
152
- async function connect(identity, local, mutator, location, dependencies) {
153
+ async function connect(identity, local, mutator, location, dependencies, contextAuthority) {
153
154
  const state = await readConnection(location, identity);
154
155
  let challenged = state.challenged ?? await remote('challenged', 'mutation-snapshot-challenge',
155
156
  { ...Object.fromEntries(fields.map(field => [field, identity[field]])),
@@ -178,7 +179,7 @@ async function connect(identity, local, mutator, location, dependencies) {
178
179
  passBinding(state.issued.pass, challenge, state.attestation);
179
180
  return connected(identity, state);
180
181
  }
181
- await snapshot(input, challenge, state, location);
182
+ await snapshot(input, challenge, state, location, contextAuthority);
182
183
  if (!state.attestation) {
183
184
  const observed = await attestMutationSnapshot14({ ...input, challenge, signerConfigPath: mutator.signerConfigPath });
184
185
  if (observed.verified !== true || observed.attestation.keyId !== mutator.mutationKeyId) fail('Actual mutation snapshot was not signed by its separate M key');
@@ -209,11 +210,13 @@ export async function connectMutation(repositoryRoot, input, dependencies) {
209
210
  if (target.exists && !target.stat.isFile()) fail('Mutation connection permits only an absent approved creation or a concrete existing file');
210
211
  }
211
212
  await bootstrapCloudMutationAuthority(dependencies);
213
+ const contextAuthority = await scopeContextObservation(local, location.root, dependencies.apiOrigin());
214
+ const observed = (await scopeContextSnapshotInput(local, location.root, contextAuthority, { initial: true })).input;
212
215
  const mutator = await preparePreservedMutationSigner(local);
213
216
  const identity = { ...original, repositoryRoot: location.root, apiOrigin: trustedOrigin(dependencies.apiOrigin()), mutationKeyId: mutator.mutationKeyId };
214
217
  const directory = path.join(location.directory, 'connections'); await privateMutationDirectory14(directory, { privateFrom: location.stateRoot });
215
218
  const file = path.join(directory, `${sha256(canonical(identity))}.json`), journal = { ...location, file };
216
- return withStateLock(file, () => connect(identity, local, mutator, journal, dependencies));
219
+ return withStateLock(file, () => connect(identity, observed, mutator, journal, dependencies, contextAuthority));
217
220
  }
218
221
  function settlementBinding(output, pass, receipt, expectedSettlementId) {
219
222
  if (output?.settled !== true || typeof output.settlementId !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(output.settlementId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entrorove/skill",
3
- "version": "0.1.68",
3
+ "version": "0.1.70",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "entrorove": "cli.mjs"
@@ -89,6 +89,13 @@
89
89
  "local-task-handoff-write-receipts.mjs",
90
90
  "remote-client.mjs",
91
91
  "scope-runner-client.mjs",
92
+ "scope-context-client.mjs",
93
+ "scope-context-observer.mjs",
94
+ "scope-context-observation.mjs",
95
+ "scope-context-remote.mjs",
96
+ "scope-context-state.mjs",
97
+ "scope-context-wire.mjs",
98
+ "scope-context-release.mjs",
92
99
  "scope-amendment-client.mjs",
93
100
  "scope-amendment-state.mjs",
94
101
  "scope-amendment-wire.mjs",
@@ -304,7 +311,15 @@
304
311
  "package-archive.mjs",
305
312
  "package-replacement.mjs",
306
313
  "rollback-history.mjs",
307
- "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"
308
323
  ],
309
324
  "scripts": {
310
325
  "test": "node test/run.mjs",