openyida 2026.7.19 → 2026.7.21
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/README.md +14 -0
- package/bin/yida.js +107 -18
- package/lib/app/create-app.js +12 -1
- package/lib/app/create-form/args.js +13 -0
- package/lib/app/create-form.js +154 -67
- package/lib/app/create-page.js +45 -9
- package/lib/app/get-schema.js +65 -14
- package/lib/app/import-app.js +92 -41
- package/lib/app/publish.js +289 -556
- package/lib/app/schema-field-resolution.js +370 -0
- package/lib/app/services/app-reader.js +85 -0
- package/lib/app/services/app-service.js +235 -0
- package/lib/app/services/canvas-page-compiler.js +91 -0
- package/lib/app/services/canvas-page-schema-builder.js +149 -0
- package/lib/app/services/field-bindings.js +68 -0
- package/lib/app/services/form-compiler.js +1922 -0
- package/lib/app/services/form-mode-service.js +182 -0
- package/lib/app/services/form-schema-patcher.js +333 -0
- package/lib/app/services/form-schema-reader.js +59 -0
- package/lib/app/services/form-service.js +379 -0
- package/lib/app/services/form-validation.js +562 -0
- package/lib/app/services/native-page-compiler.js +92 -0
- package/lib/app/services/native-page-schema-builder.js +428 -0
- package/lib/app/services/page-resource-service.js +185 -0
- package/lib/app/update-app.js +18 -6
- package/lib/auth/token-auth.js +34 -3
- package/lib/auth/token-store.js +22 -4
- package/lib/bridge/bridge.js +37 -5
- package/lib/core/agent-capabilities.js +5 -3
- package/lib/core/command-manifest.js +40 -0
- package/lib/core/doctor.js +15 -4
- package/lib/core/env-cmd.js +1 -1
- package/lib/core/env.js +7 -6
- package/lib/core/legacy-schema-guard.js +408 -0
- package/lib/core/locales/en.js +8 -46
- package/lib/core/locales/zh.js +8 -46
- package/lib/core/sample.js +1 -1
- package/lib/core/utils.js +334 -66
- package/lib/core/yida-client.js +104 -3
- package/lib/formula/evaluate.js +8 -66
- package/lib/formula/field-refs.js +83 -0
- package/lib/process/configure-process.js +44 -1992
- package/lib/process/create-process.js +54 -87
- package/lib/process/services/process-compiler.js +2056 -0
- package/lib/process/services/process-reader.js +232 -0
- package/lib/process/services/process-resource-service.js +571 -0
- package/lib/process/services/process-service.js +122 -0
- package/lib/process/services/process-stage-checkpoint.js +246 -0
- package/lib/report/append.js +7 -26
- package/lib/report/http.js +31 -5
- package/lib/report/index.js +10 -2
- package/lib/samples/yida-canvas-custom-page/business-list.canvas.jsx +63 -14
- package/lib/samples/yida-canvas-custom-page/dashboard-overview.canvas.jsx +176 -32
- package/lib/samples/yida-canvas-custom-page/data-management.canvas.jsx +228 -14
- package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +204 -8
- package/lib/schema/adapters/app-adapter.js +210 -0
- package/lib/schema/adapters/form-adapter.js +1362 -0
- package/lib/schema/adapters/keys.js +19 -0
- package/lib/schema/adapters/page-adapter.js +566 -0
- package/lib/schema/adapters/process-adapter.js +519 -0
- package/lib/schema/applier.js +1878 -0
- package/lib/schema/apply-store.js +1083 -0
- package/lib/schema/command.js +645 -0
- package/lib/schema/dependency-graph.js +83 -0
- package/lib/schema/errors.js +481 -0
- package/lib/schema/hash.js +9 -0
- package/lib/schema/manifest-limits.js +177 -0
- package/lib/schema/manifest-loader.js +309 -0
- package/lib/schema/manifest-schema-v1.json +193 -0
- package/lib/schema/normalize-manifest.js +281 -0
- package/lib/schema/page-canvas-foundation.js +447 -0
- package/lib/schema/page-data-source-builder.js +561 -0
- package/lib/schema/page-foundation.js +556 -0
- package/lib/schema/page-source-loader.js +314 -0
- package/lib/schema/planner.js +691 -0
- package/lib/schema/remote-dispatch-boundary.js +39 -0
- package/lib/schema/remote-missing.js +21 -0
- package/lib/schema/remote-reader.js +189 -0
- package/lib/schema/resource-registry.js +157 -0
- package/lib/schema/server-revision.js +71 -0
- package/lib/schema/sort.js +28 -0
- package/lib/schema/state-store.js +589 -0
- package/package.json +2 -2
- package/scripts/postinstall.js +8 -12
- package/scripts/validate-package-size.js +1 -1
- package/yida-skills/SKILL.md +114 -29
- package/yida-skills/references/schema-as-code-phase1.md +99 -0
- package/yida-skills/references/setup-and-env.md +59 -59
- package/yida-skills/references/task-retrospective.md +6 -6
- package/yida-skills/skills/yida-app/SKILL.md +60 -14
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +18 -7
- package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +3 -3
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +7 -1
- package/yida-skills/skills/yida-create-app/SKILL.md +21 -3
- package/yida-skills/skills/yida-create-form-page/SKILL.md +19 -1
- package/yida-skills/skills/yida-create-page/SKILL.md +16 -3
- package/yida-skills/skills/yida-create-process/SKILL.md +100 -4
- package/yida-skills/skills/yida-custom-page/SKILL.md +19 -4
- package/yida-skills/skills/yida-dashboard/SKILL.md +1 -1
- package/yida-skills/skills/yida-dashboard/references/pitfalls.md +1 -1
- package/yida-skills/skills/yida-get-schema/SKILL.md +49 -5
- package/yida-skills/skills/yida-i18n/SKILL.md +1 -1
- package/yida-skills/skills/yida-integration/SKILL.md +7 -1
- package/yida-skills/skills/yida-login/SKILL.md +50 -99
- package/yida-skills/skills/yida-page-config/SKILL.md +7 -1
- package/yida-skills/skills/yida-page-uiux/references/app/blueprint.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/app/navigation-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/app/role-journey.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/app-blueprint.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/chart-contracts.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/component-contracts.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/layout-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/theme-recipes.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/detail/object-narrative.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/detail/section-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/detail/timeline-and-related.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/assets-workflow.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/industry-playbooks.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/research-levels.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/section-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/drawer-detail.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/empty-loading-error.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/filter-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/table-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/scenes/screen.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/workbench/entry-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/workbench/portal-layouts.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/workbench/task-feed.md +0 -1
- package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +1 -1
- package/yida-skills/skills/yida-process-rule/SKILL.md +35 -1
- package/yida-skills/skills/yida-publish-page/SKILL.md +46 -4
- package/yida-skills/skills/yida-report/SKILL.md +7 -1
- package/yida-skills/skills-index.json +14 -46
- package/yida-skills/skills/large-file-write/SKILL.md +0 -93
- package/yida-skills/skills/large-file-write/references/write-patterns.md +0 -147
- package/yida-skills/skills/large-file-write/scripts/write.js +0 -157
- package/yida-skills/skills/sls-log-workbench/SKILL.md +0 -134
- package/yida-skills/skills/sls-log-workbench/references/workbench-reference.md +0 -130
- package/yida-skills/skills/sls-log-workbench/sls-query.js +0 -295
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { loadManifest } = require('./manifest-loader');
|
|
4
|
+
const { normalizeManifest } = require('./normalize-manifest');
|
|
5
|
+
const {
|
|
6
|
+
APPLY_KIND,
|
|
7
|
+
isTrustedSchemaError,
|
|
8
|
+
schemaError,
|
|
9
|
+
toApplyFailurePayload,
|
|
10
|
+
toFailurePayload,
|
|
11
|
+
toPlanFailurePayload,
|
|
12
|
+
toSuccessPayload,
|
|
13
|
+
} = require('./errors');
|
|
14
|
+
const { applySchema } = require('./applier');
|
|
15
|
+
const { readObservedResources } = require('./remote-reader');
|
|
16
|
+
const { createDefaultRegistry } = require('./resource-registry');
|
|
17
|
+
const { readState } = require('./state-store');
|
|
18
|
+
const {
|
|
19
|
+
PLAN_KIND,
|
|
20
|
+
createPlan,
|
|
21
|
+
selectObservableResources,
|
|
22
|
+
} = require('./planner');
|
|
23
|
+
const { isServerRevisionConflict } = require('./server-revision');
|
|
24
|
+
const {
|
|
25
|
+
extractInfoFromCookies,
|
|
26
|
+
loadAuthData,
|
|
27
|
+
resolveBaseUrl,
|
|
28
|
+
} = require('../core/utils');
|
|
29
|
+
|
|
30
|
+
const RECONCILIATION_CHOICES = Object.freeze([
|
|
31
|
+
'inspect_remote_state',
|
|
32
|
+
'provide_explicit_recovery_input',
|
|
33
|
+
'stop',
|
|
34
|
+
]);
|
|
35
|
+
const MANAGED_DECISION_CHOICES = Object.freeze([
|
|
36
|
+
'keep_remote',
|
|
37
|
+
'update_manifest',
|
|
38
|
+
'stop',
|
|
39
|
+
]);
|
|
40
|
+
const SECURITY_FAILURE_CODES = new Set([
|
|
41
|
+
'SCHEMA_APPLY_LOCKED',
|
|
42
|
+
'SCHEMA_APPLY_LOCK_FAILED',
|
|
43
|
+
'SCHEMA_APPLY_LOCK_LOST',
|
|
44
|
+
'SCHEMA_APPLY_PATH_UNSAFE',
|
|
45
|
+
'SCHEMA_FORBIDDEN_FIELD',
|
|
46
|
+
'SCHEMA_MANIFEST_PATH_UNSAFE',
|
|
47
|
+
'SCHEMA_PAGE_SOURCE_PATH_UNSAFE',
|
|
48
|
+
'SCHEMA_PLAN_STATE_INTEGRITY_FAILED',
|
|
49
|
+
'SCHEMA_STATE_ENVIRONMENT_MISMATCH',
|
|
50
|
+
'SCHEMA_STATE_FORBIDDEN_FIELD',
|
|
51
|
+
]);
|
|
52
|
+
const VALIDATION_FAILURE_CODES = new Set([
|
|
53
|
+
'SCHEMA_APPLY_JOURNAL_INVALID',
|
|
54
|
+
'SCHEMA_DUPLICATE_KEY',
|
|
55
|
+
'SCHEMA_INVALID',
|
|
56
|
+
'SCHEMA_INVALID_ARGUMENTS',
|
|
57
|
+
'SCHEMA_INVALID_KEY',
|
|
58
|
+
'SCHEMA_INVALID_KIND',
|
|
59
|
+
'SCHEMA_INVALID_REFERENCE',
|
|
60
|
+
'SCHEMA_PAGE_FOUNDATION_INITIAL_PROFILE_INVALID',
|
|
61
|
+
'SCHEMA_PAGE_FOUNDATION_OBSERVED_IDENTITY_INVALID',
|
|
62
|
+
'SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID',
|
|
63
|
+
'SCHEMA_PAGE_FOUNDATION_SHELL_PROFILE_INVALID',
|
|
64
|
+
'SCHEMA_PAGE_FOUNDATION_TRANSITION_INVALID',
|
|
65
|
+
'SCHEMA_PAGE_SOURCE_INVALID',
|
|
66
|
+
'SCHEMA_PROCESS_NODE_PROPERTY_INVALID',
|
|
67
|
+
'SCHEMA_STATE_INVALID',
|
|
68
|
+
'SCHEMA_UNKNOWN_PROPERTY',
|
|
69
|
+
]);
|
|
70
|
+
const VALIDATION_FAILURE_PREFIXES = Object.freeze([
|
|
71
|
+
'SCHEMA_DEPENDENCY_',
|
|
72
|
+
'SCHEMA_FIELD_',
|
|
73
|
+
'SCHEMA_MANIFEST_',
|
|
74
|
+
'SCHEMA_REFERENCE_',
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
function parseArgs(args) {
|
|
78
|
+
const [subCommand, ...rest] = args;
|
|
79
|
+
if (!subCommand || subCommand === '--help' || subCommand === '-h') {
|
|
80
|
+
return { help: true };
|
|
81
|
+
}
|
|
82
|
+
if (subCommand === 'validate') {
|
|
83
|
+
return parseValidateArgs(rest);
|
|
84
|
+
}
|
|
85
|
+
if (subCommand === 'plan') {
|
|
86
|
+
return parsePlanArgs(rest);
|
|
87
|
+
}
|
|
88
|
+
if (subCommand === 'apply') {
|
|
89
|
+
return parseApplyArgs(rest);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Supported schema commands are "validate", "plan", and "apply".');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function parseValidateArgs(rest) {
|
|
96
|
+
const flags = new Set();
|
|
97
|
+
const positional = [];
|
|
98
|
+
for (const arg of rest) {
|
|
99
|
+
if (arg.startsWith('--')) {
|
|
100
|
+
if (arg !== '--json' && arg !== '--quiet') {
|
|
101
|
+
throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Unknown schema validate option.', {
|
|
102
|
+
details: { option: arg },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
flags.add(arg);
|
|
106
|
+
} else {
|
|
107
|
+
positional.push(arg);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (positional.length !== 1) {
|
|
112
|
+
throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Usage: openyida schema validate <manifest> --json --quiet.');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
subCommand: 'validate',
|
|
117
|
+
manifestPath: positional[0],
|
|
118
|
+
json: flags.has('--json'),
|
|
119
|
+
quiet: flags.has('--quiet'),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function parsePlanArgs(rest) {
|
|
124
|
+
const flags = new Set();
|
|
125
|
+
const positional = [];
|
|
126
|
+
let statePath;
|
|
127
|
+
|
|
128
|
+
for (let index = 0; index < rest.length; index++) {
|
|
129
|
+
const arg = rest[index];
|
|
130
|
+
if (arg === '--state') {
|
|
131
|
+
statePath = rest[index + 1];
|
|
132
|
+
index += 1;
|
|
133
|
+
if (!statePath || statePath.startsWith('--')) {
|
|
134
|
+
throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Usage: openyida schema plan <manifest> --state <path> --json --quiet.');
|
|
135
|
+
}
|
|
136
|
+
} else if (arg.startsWith('--')) {
|
|
137
|
+
if (arg !== '--json' && arg !== '--quiet') {
|
|
138
|
+
throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Unknown schema plan option.', {
|
|
139
|
+
details: { option: arg },
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
flags.add(arg);
|
|
143
|
+
} else {
|
|
144
|
+
positional.push(arg);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (positional.length !== 1 || !statePath) {
|
|
149
|
+
throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Usage: openyida schema plan <manifest> --state <path> --json --quiet.');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
subCommand: 'plan',
|
|
154
|
+
manifestPath: positional[0],
|
|
155
|
+
statePath,
|
|
156
|
+
json: flags.has('--json'),
|
|
157
|
+
quiet: flags.has('--quiet'),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function parseApplyArgs(rest) {
|
|
162
|
+
const flags = new Set();
|
|
163
|
+
const positional = [];
|
|
164
|
+
let statePath;
|
|
165
|
+
let planId;
|
|
166
|
+
|
|
167
|
+
for (let index = 0; index < rest.length; index++) {
|
|
168
|
+
const arg = rest[index];
|
|
169
|
+
if (arg === '--state' || arg === '--plan-id') {
|
|
170
|
+
const value = rest[index + 1];
|
|
171
|
+
index += 1;
|
|
172
|
+
if (!value || value.startsWith('--')) {
|
|
173
|
+
throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Usage: openyida schema apply <manifest> --state <path> --plan-id <planId> --json --quiet.');
|
|
174
|
+
}
|
|
175
|
+
if (arg === '--state') {
|
|
176
|
+
statePath = value;
|
|
177
|
+
} else {
|
|
178
|
+
planId = value;
|
|
179
|
+
}
|
|
180
|
+
} else if (arg.startsWith('--')) {
|
|
181
|
+
if (arg !== '--json' && arg !== '--quiet') {
|
|
182
|
+
throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Unknown schema apply option.', {
|
|
183
|
+
details: { option: arg },
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
flags.add(arg);
|
|
187
|
+
} else {
|
|
188
|
+
positional.push(arg);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (
|
|
193
|
+
positional.length !== 1 ||
|
|
194
|
+
!statePath ||
|
|
195
|
+
!/^sha256:[a-f0-9]{64}$/.test(planId || '')
|
|
196
|
+
) {
|
|
197
|
+
throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Usage: openyida schema apply <manifest> --state <path> --plan-id <planId> --json --quiet.');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
subCommand: 'apply',
|
|
202
|
+
manifestPath: positional[0],
|
|
203
|
+
statePath,
|
|
204
|
+
planId,
|
|
205
|
+
json: flags.has('--json'),
|
|
206
|
+
quiet: flags.has('--quiet'),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function run(args = [], io = {}) {
|
|
211
|
+
const stdout = io.stdout || process.stdout;
|
|
212
|
+
const stderr = io.stderr || process.stderr;
|
|
213
|
+
const setExitCode = io.setExitCode !== false;
|
|
214
|
+
|
|
215
|
+
let options;
|
|
216
|
+
try {
|
|
217
|
+
options = parseArgs(args);
|
|
218
|
+
if (options.help) {
|
|
219
|
+
stdout.write('Usage: openyida schema <validate|plan|apply> <manifest> [--state <path>] [--plan-id <planId>] --json --quiet\n');
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (options.subCommand === 'apply') {
|
|
224
|
+
const payload = await runApply(options, io);
|
|
225
|
+
stdout.write(JSON.stringify(payload) + '\n');
|
|
226
|
+
return payload;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (options.subCommand === 'plan') {
|
|
230
|
+
const payload = await runPlan(options, io);
|
|
231
|
+
stdout.write(JSON.stringify(payload) + '\n');
|
|
232
|
+
return payload;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const manifestOptions = createManifestLoadOptions(io);
|
|
236
|
+
const manifest = loadManifest(options.manifestPath, manifestOptions);
|
|
237
|
+
const result = normalizeManifest(manifest, manifestOptions);
|
|
238
|
+
const payload = toSuccessPayload(result);
|
|
239
|
+
stdout.write(JSON.stringify(payload) + '\n');
|
|
240
|
+
return payload;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
const payloadKind = inferPayloadKind(args, options);
|
|
243
|
+
let payload = payloadKind === APPLY_KIND
|
|
244
|
+
? toApplyFailurePayload(error)
|
|
245
|
+
: payloadKind === PLAN_KIND
|
|
246
|
+
? toPlanFailurePayload(error)
|
|
247
|
+
: toFailurePayload(error);
|
|
248
|
+
if (payloadKind === APPLY_KIND && shouldAutomaticallyReplan(error) && options) {
|
|
249
|
+
payload = await attachAutomaticReplan(payload, error, options, io);
|
|
250
|
+
} else {
|
|
251
|
+
payload = attachFailureAction(payload, error);
|
|
252
|
+
}
|
|
253
|
+
const quiet = args.includes('--quiet') || process.argv.includes('--quiet') || (options && options.quiet);
|
|
254
|
+
stdout.write(JSON.stringify(payload) + '\n');
|
|
255
|
+
if (setExitCode) {
|
|
256
|
+
process.exitCode = 1;
|
|
257
|
+
}
|
|
258
|
+
if (!quiet && stderr && stderr.write) {
|
|
259
|
+
stderr.write(`${payload.error.code}: ${payload.error.message}\n`);
|
|
260
|
+
}
|
|
261
|
+
return payload;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function runPlan(options, io) {
|
|
266
|
+
const registry = io.registry || createDefaultRegistry();
|
|
267
|
+
const runtime = createPlanRuntimeSnapshot(io);
|
|
268
|
+
const manifestOptions = createManifestLoadOptions(io);
|
|
269
|
+
const manifest = loadManifest(options.manifestPath, manifestOptions);
|
|
270
|
+
const normalizedResult = normalizeManifest(manifest, {
|
|
271
|
+
...manifestOptions,
|
|
272
|
+
fsImpl: io.fsImpl,
|
|
273
|
+
registry,
|
|
274
|
+
});
|
|
275
|
+
const desiredResources = normalizedResult.normalized.resources;
|
|
276
|
+
const state = io.state || readState(options.statePath, {
|
|
277
|
+
environment: runtime.environment,
|
|
278
|
+
fsImpl: io.fsImpl,
|
|
279
|
+
manifestHash: normalizedResult.manifestHash,
|
|
280
|
+
registry,
|
|
281
|
+
});
|
|
282
|
+
const observableResources = selectObservableResources(desiredResources, state, { registry });
|
|
283
|
+
const observed = await resolveObservedResources(observableResources, state, {
|
|
284
|
+
io,
|
|
285
|
+
registry,
|
|
286
|
+
runtime,
|
|
287
|
+
});
|
|
288
|
+
return attachPlanAction(createPlan({
|
|
289
|
+
desiredResources,
|
|
290
|
+
manifestHash: normalizedResult.manifestHash,
|
|
291
|
+
observedResources: observed.resources,
|
|
292
|
+
state,
|
|
293
|
+
}, { registry }));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function attachAutomaticReplan(payload, error, options, io) {
|
|
297
|
+
let replanned;
|
|
298
|
+
try {
|
|
299
|
+
replanned = await runPlan({
|
|
300
|
+
manifestPath: options.manifestPath,
|
|
301
|
+
statePath: options.statePath,
|
|
302
|
+
subCommand: 'plan',
|
|
303
|
+
}, io);
|
|
304
|
+
} catch (replanError) {
|
|
305
|
+
const replanFailure = attachFailureAction(toPlanFailurePayload(replanError), replanError);
|
|
306
|
+
return {
|
|
307
|
+
...payload,
|
|
308
|
+
action: createAction(payload.error.code, 'automatic_replan_failed', {
|
|
309
|
+
blockText: replanFailure.error.message,
|
|
310
|
+
choices: replanFailure.action.choices,
|
|
311
|
+
nextAction: replanFailure.action.nextAction,
|
|
312
|
+
}),
|
|
313
|
+
replan: replanFailure,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const reviewedAction = replanned.action;
|
|
318
|
+
return {
|
|
319
|
+
...payload,
|
|
320
|
+
action: createAction(payload.error.code, 'stale_replanned', {
|
|
321
|
+
blockText: payload.error.message,
|
|
322
|
+
choices: reviewedAction && reviewedAction.choices,
|
|
323
|
+
nextAction: reviewedAction && reviewedAction.nextAction || 'review_replanned_plan',
|
|
324
|
+
}),
|
|
325
|
+
replan: replanned,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function shouldAutomaticallyReplan(error) {
|
|
330
|
+
return !!(
|
|
331
|
+
error &&
|
|
332
|
+
(isFormOrPageServerRevisionConflict(error) || (
|
|
333
|
+
error.code === 'SCHEMA_APPLY_PLAN_STALE' &&
|
|
334
|
+
isTrustedSchemaError(error)
|
|
335
|
+
))
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function isFormOrPageServerRevisionConflict(error) {
|
|
340
|
+
return isServerRevisionConflict(error) &&
|
|
341
|
+
!!error.details &&
|
|
342
|
+
(error.details.resourceType === 'form' || error.details.resourceType === 'page');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function attachPlanAction(plan) {
|
|
346
|
+
const operations = new Set((plan.changes || []).map(change => change.operation));
|
|
347
|
+
if (operations.has('conflict') || operations.has('unmanaged')) {
|
|
348
|
+
return {
|
|
349
|
+
...plan,
|
|
350
|
+
action: createAction('SCHEMA_APPLY_BLOCKED', 'managed_decision_required', {
|
|
351
|
+
blockText: safeApplyBlockText('SCHEMA_APPLY_BLOCKED'),
|
|
352
|
+
choices: MANAGED_DECISION_CHOICES,
|
|
353
|
+
nextAction: 'ask_human',
|
|
354
|
+
}),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
if (operations.has('orphan')) {
|
|
358
|
+
return {
|
|
359
|
+
...plan,
|
|
360
|
+
action: createAction('SCHEMA_APPLY_BLOCKED', 'orphan_blocked', {
|
|
361
|
+
blockText: safeApplyBlockText('SCHEMA_APPLY_BLOCKED'),
|
|
362
|
+
nextAction: 'resolve_orphan',
|
|
363
|
+
}),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
return plan;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function attachFailureAction(payload, error) {
|
|
370
|
+
return {
|
|
371
|
+
...payload,
|
|
372
|
+
action: classifyFailureAction(payload, error),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function classifyFailureAction(payload, _error) {
|
|
377
|
+
const errorCode = payload.error.code;
|
|
378
|
+
const blockText = payload.error.message;
|
|
379
|
+
const operation = payload.error.details && payload.error.details.operation;
|
|
380
|
+
|
|
381
|
+
if (errorCode === 'SCHEMA_RECONCILIATION_REQUIRED' || /UNCERTAIN/.test(errorCode)) {
|
|
382
|
+
return createAction(errorCode, 'reconciliation_required', {
|
|
383
|
+
blockText,
|
|
384
|
+
choices: RECONCILIATION_CHOICES,
|
|
385
|
+
nextAction: 'ask_human',
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
if (
|
|
389
|
+
errorCode === 'SCHEMA_STATE_RESOURCE_BINDING_MISSING' ||
|
|
390
|
+
errorCode === 'SCHEMA_OBSERVED_BINDING_MISSING' ||
|
|
391
|
+
errorCode === 'SCHEMA_OBSERVED_REFERENCE_AMBIGUOUS' ||
|
|
392
|
+
errorCode === 'SCHEMA_OBSERVED_REFERENCE_MISSING' ||
|
|
393
|
+
errorCode === 'SCHEMA_REMOTE_RESOURCE_MISSING'
|
|
394
|
+
) {
|
|
395
|
+
return createAction(errorCode, 'managed_identity_decision_required', {
|
|
396
|
+
blockText,
|
|
397
|
+
choices: RECONCILIATION_CHOICES,
|
|
398
|
+
nextAction: 'ask_human',
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
if (
|
|
402
|
+
errorCode === 'SCHEMA_APPLY_BLOCKED' &&
|
|
403
|
+
(operation === 'conflict' || operation === 'unmanaged')
|
|
404
|
+
) {
|
|
405
|
+
return createAction(errorCode, 'managed_decision_required', {
|
|
406
|
+
blockText,
|
|
407
|
+
choices: MANAGED_DECISION_CHOICES,
|
|
408
|
+
nextAction: 'ask_human',
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
if (errorCode === 'SCHEMA_APPLY_BLOCKED' && operation === 'orphan') {
|
|
412
|
+
return createAction(errorCode, 'orphan_blocked', {
|
|
413
|
+
blockText,
|
|
414
|
+
nextAction: 'resolve_orphan',
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
if (errorCode === 'SCHEMA_APPLY_JIT_CONFLICT') {
|
|
418
|
+
return createAction(errorCode, 'jit_conflict', {
|
|
419
|
+
blockText,
|
|
420
|
+
nextAction: 'run_schema_plan',
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
if (errorCode === 'SCHEMA_APPLY_PLAN_STALE') {
|
|
424
|
+
return createAction(errorCode, 'stale_blocked', {
|
|
425
|
+
blockText,
|
|
426
|
+
nextAction: 'run_schema_plan',
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
if (/UNSUPPORTED/.test(errorCode)) {
|
|
430
|
+
return createAction(errorCode, 'unsupported', {
|
|
431
|
+
blockText,
|
|
432
|
+
nextAction: 'remove_unsupported_manifest_content',
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
if (isSecurityFailure(errorCode)) {
|
|
436
|
+
return createAction(errorCode, 'security_failure', {
|
|
437
|
+
blockText,
|
|
438
|
+
nextAction: 'fix_security_boundary',
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
if (isValidationFailure(errorCode)) {
|
|
442
|
+
return createAction(errorCode, 'validation_failure', {
|
|
443
|
+
blockText,
|
|
444
|
+
nextAction: 'fix_manifest',
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
if (errorCode === 'SCHEMA_REMOTE_READ_FAILED' || errorCode === 'SCHEMA_REMOTE_PROJECT_FAILED') {
|
|
448
|
+
return createAction(errorCode, 'remote_read_blocked', {
|
|
449
|
+
blockText,
|
|
450
|
+
nextAction: 'check_remote_read',
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return createAction(errorCode, 'blocked', {
|
|
454
|
+
blockText,
|
|
455
|
+
nextAction: 'stop',
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function createAction(errorCode, classification, options) {
|
|
460
|
+
const action = {
|
|
461
|
+
errorCode,
|
|
462
|
+
classification,
|
|
463
|
+
safeToRetry: false,
|
|
464
|
+
nextAction: options.nextAction,
|
|
465
|
+
blockText: options.blockText,
|
|
466
|
+
};
|
|
467
|
+
if (Array.isArray(options.choices) && options.choices.length > 0) {
|
|
468
|
+
action.choices = options.choices.slice();
|
|
469
|
+
}
|
|
470
|
+
return action;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function safeApplyBlockText(code) {
|
|
474
|
+
return toApplyFailurePayload(schemaError(code, 'Schema apply is blocked.')).error.message;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function isSecurityFailure(code) {
|
|
478
|
+
return SECURITY_FAILURE_CODES.has(code);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function isValidationFailure(code) {
|
|
482
|
+
return VALIDATION_FAILURE_CODES.has(code) || hasCodePrefix(code, VALIDATION_FAILURE_PREFIXES);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function hasCodePrefix(code, prefixes) {
|
|
486
|
+
return prefixes.some(prefix => typeof code === 'string' && code.startsWith(prefix));
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async function runApply(options, io) {
|
|
490
|
+
const registry = io.registry || createDefaultRegistry();
|
|
491
|
+
const runtime = createPlanRuntimeSnapshot(io);
|
|
492
|
+
if (!runtime.authRef && !hasInjectedServices(io.services)) {
|
|
493
|
+
throw schemaError('SCHEMA_STATE_ENVIRONMENT_REQUIRED', 'Schema apply requires environment identity and Yida auth context.', {
|
|
494
|
+
path: '/environment',
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
return applySchema({
|
|
498
|
+
authRef: runtime.authRef,
|
|
499
|
+
environment: runtime.environment,
|
|
500
|
+
expectedPlanId: options.planId,
|
|
501
|
+
loadDesired() {
|
|
502
|
+
const manifestOptions = createManifestLoadOptions(io);
|
|
503
|
+
const manifest = loadManifest(options.manifestPath, manifestOptions);
|
|
504
|
+
return normalizeManifest(manifest, {
|
|
505
|
+
...manifestOptions,
|
|
506
|
+
fsImpl: io.fsImpl,
|
|
507
|
+
registry,
|
|
508
|
+
});
|
|
509
|
+
},
|
|
510
|
+
statePath: options.statePath,
|
|
511
|
+
workspaceRoot: io.projectRoot || process.cwd(),
|
|
512
|
+
}, {
|
|
513
|
+
fsImpl: io.fsImpl,
|
|
514
|
+
paths: io.applyPaths,
|
|
515
|
+
readObservedResources: io.readObservedResources,
|
|
516
|
+
formPostWriteReadbackRetry: options.formPostWriteReadbackRetry ?? io.formPostWriteReadbackRetry,
|
|
517
|
+
registry,
|
|
518
|
+
services: io.services,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function createManifestLoadOptions(io = {}) {
|
|
523
|
+
const options = {
|
|
524
|
+
workspaceRoot: io.projectRoot || process.cwd(),
|
|
525
|
+
};
|
|
526
|
+
if (io.fsImpl) {
|
|
527
|
+
options.fsImpl = io.fsImpl;
|
|
528
|
+
}
|
|
529
|
+
if (io.manifestLimits) {
|
|
530
|
+
options.limits = io.manifestLimits;
|
|
531
|
+
}
|
|
532
|
+
return options;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function hasInjectedServices(services) {
|
|
536
|
+
return !!(services && Object.keys(services).length > 0);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function resolveObservedResources(resources, state, context) {
|
|
540
|
+
const io = context.io || {};
|
|
541
|
+
const runtime = context.runtime || createPlanRuntimeSnapshot(io);
|
|
542
|
+
if (io.observed) {
|
|
543
|
+
return io.observed;
|
|
544
|
+
}
|
|
545
|
+
if (io.observedResources) {
|
|
546
|
+
return { resources: io.observedResources };
|
|
547
|
+
}
|
|
548
|
+
if (typeof io.readObservedResources === 'function') {
|
|
549
|
+
return io.readObservedResources(resources, state, {
|
|
550
|
+
context: Object.assign({}, io.observedContext || {}, {
|
|
551
|
+
authRef: runtime.authRef,
|
|
552
|
+
environment: runtime.environment,
|
|
553
|
+
}),
|
|
554
|
+
registry: context.registry,
|
|
555
|
+
services: io.services,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
if (!resources || resources.length === 0) {
|
|
559
|
+
return { resources: [] };
|
|
560
|
+
}
|
|
561
|
+
const authRef = runtime.authRef;
|
|
562
|
+
if (!authRef && !io.services) {
|
|
563
|
+
throw schemaError('SCHEMA_STATE_ENVIRONMENT_REQUIRED', 'Schema plan requires environment identity and read-only Yida auth context.', {
|
|
564
|
+
path: '/environment',
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
return readObservedResources(resources, state, {
|
|
568
|
+
context: Object.assign({}, io.observedContext || {}, {
|
|
569
|
+
authRef,
|
|
570
|
+
environment: runtime.environment,
|
|
571
|
+
}),
|
|
572
|
+
registry: context.registry,
|
|
573
|
+
services: io.services,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function createPlanRuntimeSnapshot(io) {
|
|
578
|
+
const authRef = resolvePlanAuthRef(io);
|
|
579
|
+
return {
|
|
580
|
+
authRef,
|
|
581
|
+
environment: resolvePlanEnvironment(io, authRef),
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function resolvePlanEnvironment(io, authRef) {
|
|
586
|
+
if (io.environment) {
|
|
587
|
+
return io.environment;
|
|
588
|
+
}
|
|
589
|
+
const cookieData = io.cookieData !== undefined
|
|
590
|
+
? io.cookieData
|
|
591
|
+
: authRef && authRef.cookieData;
|
|
592
|
+
const cookies = Array.isArray(cookieData && cookieData.cookies)
|
|
593
|
+
? cookieData.cookies
|
|
594
|
+
: authRef && authRef.cookies || [];
|
|
595
|
+
const cookieInfo = extractInfoFromCookies(cookies);
|
|
596
|
+
return {
|
|
597
|
+
endpoint: authRef && authRef.baseUrl || resolveBaseUrl(cookieData || {}),
|
|
598
|
+
corpId: authRef && authRef.corpId || cookieData && (cookieData.corp_id || cookieData.corpId) || cookieInfo.corpId,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function resolvePlanAuthRef(io) {
|
|
603
|
+
if (io.authRef) {
|
|
604
|
+
return io.authRef;
|
|
605
|
+
}
|
|
606
|
+
const authData = loadAuthData(io.projectRoot);
|
|
607
|
+
if (authData && (authData.auth_mode === 'token' || authData.authMode === 'token')) {
|
|
608
|
+
return {
|
|
609
|
+
baseUrl: resolveBaseUrl(authData),
|
|
610
|
+
authData,
|
|
611
|
+
authMode: 'token',
|
|
612
|
+
authSource: authData.auth_source || authData.authSource || '',
|
|
613
|
+
corpId: authData.corp_id || authData.corpId || '',
|
|
614
|
+
userId: authData.user_id || authData.userId || '',
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
const cookieData = io.cookieData;
|
|
618
|
+
if (!cookieData || !Array.isArray(cookieData.cookies) || cookieData.cookies.length === 0) {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
const cookieInfo = extractInfoFromCookies(cookieData.cookies);
|
|
622
|
+
return {
|
|
623
|
+
baseUrl: resolveBaseUrl(cookieData),
|
|
624
|
+
cookieData,
|
|
625
|
+
cookies: cookieData.cookies,
|
|
626
|
+
corpId: cookieData.corp_id || cookieData.corpId || cookieInfo.corpId || '',
|
|
627
|
+
csrfToken: cookieData.csrf_token || cookieData.csrfToken || cookieData._csrf_token || cookieInfo.csrfToken || '',
|
|
628
|
+
userId: cookieData.user_id || cookieData.userId || cookieData.staffId || cookieInfo.userId || '',
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function inferPayloadKind(args, options) {
|
|
633
|
+
if ((options && options.subCommand === 'apply') || args[0] === 'apply') {
|
|
634
|
+
return APPLY_KIND;
|
|
635
|
+
}
|
|
636
|
+
if ((options && options.subCommand === 'plan') || args[0] === 'plan') {
|
|
637
|
+
return PLAN_KIND;
|
|
638
|
+
}
|
|
639
|
+
return undefined;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
module.exports = {
|
|
643
|
+
parseArgs,
|
|
644
|
+
run,
|
|
645
|
+
};
|