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.
Files changed (139) hide show
  1. package/README.md +14 -0
  2. package/bin/yida.js +107 -18
  3. package/lib/app/create-app.js +12 -1
  4. package/lib/app/create-form/args.js +13 -0
  5. package/lib/app/create-form.js +154 -67
  6. package/lib/app/create-page.js +45 -9
  7. package/lib/app/get-schema.js +65 -14
  8. package/lib/app/import-app.js +92 -41
  9. package/lib/app/publish.js +289 -556
  10. package/lib/app/schema-field-resolution.js +370 -0
  11. package/lib/app/services/app-reader.js +85 -0
  12. package/lib/app/services/app-service.js +235 -0
  13. package/lib/app/services/canvas-page-compiler.js +91 -0
  14. package/lib/app/services/canvas-page-schema-builder.js +149 -0
  15. package/lib/app/services/field-bindings.js +68 -0
  16. package/lib/app/services/form-compiler.js +1922 -0
  17. package/lib/app/services/form-mode-service.js +182 -0
  18. package/lib/app/services/form-schema-patcher.js +333 -0
  19. package/lib/app/services/form-schema-reader.js +59 -0
  20. package/lib/app/services/form-service.js +379 -0
  21. package/lib/app/services/form-validation.js +562 -0
  22. package/lib/app/services/native-page-compiler.js +92 -0
  23. package/lib/app/services/native-page-schema-builder.js +428 -0
  24. package/lib/app/services/page-resource-service.js +185 -0
  25. package/lib/app/update-app.js +18 -6
  26. package/lib/auth/token-auth.js +34 -3
  27. package/lib/auth/token-store.js +22 -4
  28. package/lib/bridge/bridge.js +37 -5
  29. package/lib/core/agent-capabilities.js +5 -3
  30. package/lib/core/command-manifest.js +40 -0
  31. package/lib/core/doctor.js +15 -4
  32. package/lib/core/env-cmd.js +1 -1
  33. package/lib/core/env.js +7 -6
  34. package/lib/core/legacy-schema-guard.js +408 -0
  35. package/lib/core/locales/en.js +8 -46
  36. package/lib/core/locales/zh.js +8 -46
  37. package/lib/core/sample.js +1 -1
  38. package/lib/core/utils.js +334 -66
  39. package/lib/core/yida-client.js +104 -3
  40. package/lib/formula/evaluate.js +8 -66
  41. package/lib/formula/field-refs.js +83 -0
  42. package/lib/process/configure-process.js +44 -1992
  43. package/lib/process/create-process.js +54 -87
  44. package/lib/process/services/process-compiler.js +2056 -0
  45. package/lib/process/services/process-reader.js +232 -0
  46. package/lib/process/services/process-resource-service.js +571 -0
  47. package/lib/process/services/process-service.js +122 -0
  48. package/lib/process/services/process-stage-checkpoint.js +246 -0
  49. package/lib/report/append.js +7 -26
  50. package/lib/report/http.js +31 -5
  51. package/lib/report/index.js +10 -2
  52. package/lib/samples/yida-canvas-custom-page/business-list.canvas.jsx +63 -14
  53. package/lib/samples/yida-canvas-custom-page/dashboard-overview.canvas.jsx +176 -32
  54. package/lib/samples/yida-canvas-custom-page/data-management.canvas.jsx +228 -14
  55. package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +204 -8
  56. package/lib/schema/adapters/app-adapter.js +210 -0
  57. package/lib/schema/adapters/form-adapter.js +1362 -0
  58. package/lib/schema/adapters/keys.js +19 -0
  59. package/lib/schema/adapters/page-adapter.js +566 -0
  60. package/lib/schema/adapters/process-adapter.js +519 -0
  61. package/lib/schema/applier.js +1878 -0
  62. package/lib/schema/apply-store.js +1083 -0
  63. package/lib/schema/command.js +645 -0
  64. package/lib/schema/dependency-graph.js +83 -0
  65. package/lib/schema/errors.js +481 -0
  66. package/lib/schema/hash.js +9 -0
  67. package/lib/schema/manifest-limits.js +177 -0
  68. package/lib/schema/manifest-loader.js +309 -0
  69. package/lib/schema/manifest-schema-v1.json +193 -0
  70. package/lib/schema/normalize-manifest.js +281 -0
  71. package/lib/schema/page-canvas-foundation.js +447 -0
  72. package/lib/schema/page-data-source-builder.js +561 -0
  73. package/lib/schema/page-foundation.js +556 -0
  74. package/lib/schema/page-source-loader.js +314 -0
  75. package/lib/schema/planner.js +691 -0
  76. package/lib/schema/remote-dispatch-boundary.js +39 -0
  77. package/lib/schema/remote-missing.js +21 -0
  78. package/lib/schema/remote-reader.js +189 -0
  79. package/lib/schema/resource-registry.js +157 -0
  80. package/lib/schema/server-revision.js +71 -0
  81. package/lib/schema/sort.js +28 -0
  82. package/lib/schema/state-store.js +589 -0
  83. package/package.json +2 -2
  84. package/scripts/postinstall.js +8 -12
  85. package/scripts/validate-package-size.js +1 -1
  86. package/yida-skills/SKILL.md +114 -29
  87. package/yida-skills/references/schema-as-code-phase1.md +99 -0
  88. package/yida-skills/references/setup-and-env.md +59 -59
  89. package/yida-skills/references/task-retrospective.md +6 -6
  90. package/yida-skills/skills/yida-app/SKILL.md +60 -14
  91. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +18 -7
  92. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +3 -3
  93. package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +7 -1
  94. package/yida-skills/skills/yida-create-app/SKILL.md +21 -3
  95. package/yida-skills/skills/yida-create-form-page/SKILL.md +19 -1
  96. package/yida-skills/skills/yida-create-page/SKILL.md +16 -3
  97. package/yida-skills/skills/yida-create-process/SKILL.md +100 -4
  98. package/yida-skills/skills/yida-custom-page/SKILL.md +19 -4
  99. package/yida-skills/skills/yida-dashboard/SKILL.md +1 -1
  100. package/yida-skills/skills/yida-dashboard/references/pitfalls.md +1 -1
  101. package/yida-skills/skills/yida-get-schema/SKILL.md +49 -5
  102. package/yida-skills/skills/yida-i18n/SKILL.md +1 -1
  103. package/yida-skills/skills/yida-integration/SKILL.md +7 -1
  104. package/yida-skills/skills/yida-login/SKILL.md +50 -99
  105. package/yida-skills/skills/yida-page-config/SKILL.md +7 -1
  106. package/yida-skills/skills/yida-page-uiux/references/app/blueprint.md +0 -1
  107. package/yida-skills/skills/yida-page-uiux/references/app/navigation-patterns.md +0 -1
  108. package/yida-skills/skills/yida-page-uiux/references/app/role-journey.md +0 -1
  109. package/yida-skills/skills/yida-page-uiux/references/dashboard/app-blueprint.md +0 -1
  110. package/yida-skills/skills/yida-page-uiux/references/dashboard/chart-contracts.md +0 -1
  111. package/yida-skills/skills/yida-page-uiux/references/dashboard/component-contracts.md +0 -1
  112. package/yida-skills/skills/yida-page-uiux/references/dashboard/layout-patterns.md +0 -1
  113. package/yida-skills/skills/yida-page-uiux/references/dashboard/theme-recipes.md +0 -1
  114. package/yida-skills/skills/yida-page-uiux/references/detail/object-narrative.md +0 -1
  115. package/yida-skills/skills/yida-page-uiux/references/detail/section-patterns.md +0 -1
  116. package/yida-skills/skills/yida-page-uiux/references/detail/timeline-and-related.md +0 -1
  117. package/yida-skills/skills/yida-page-uiux/references/landing/assets-workflow.md +0 -1
  118. package/yida-skills/skills/yida-page-uiux/references/landing/industry-playbooks.md +0 -1
  119. package/yida-skills/skills/yida-page-uiux/references/landing/research-levels.md +0 -1
  120. package/yida-skills/skills/yida-page-uiux/references/landing/section-patterns.md +0 -1
  121. package/yida-skills/skills/yida-page-uiux/references/list/drawer-detail.md +0 -1
  122. package/yida-skills/skills/yida-page-uiux/references/list/empty-loading-error.md +0 -1
  123. package/yida-skills/skills/yida-page-uiux/references/list/filter-patterns.md +0 -1
  124. package/yida-skills/skills/yida-page-uiux/references/list/table-patterns.md +0 -1
  125. package/yida-skills/skills/yida-page-uiux/references/scenes/screen.md +0 -1
  126. package/yida-skills/skills/yida-page-uiux/references/workbench/entry-patterns.md +0 -1
  127. package/yida-skills/skills/yida-page-uiux/references/workbench/portal-layouts.md +0 -1
  128. package/yida-skills/skills/yida-page-uiux/references/workbench/task-feed.md +0 -1
  129. package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +1 -1
  130. package/yida-skills/skills/yida-process-rule/SKILL.md +35 -1
  131. package/yida-skills/skills/yida-publish-page/SKILL.md +46 -4
  132. package/yida-skills/skills/yida-report/SKILL.md +7 -1
  133. package/yida-skills/skills-index.json +14 -46
  134. package/yida-skills/skills/large-file-write/SKILL.md +0 -93
  135. package/yida-skills/skills/large-file-write/references/write-patterns.md +0 -147
  136. package/yida-skills/skills/large-file-write/scripts/write.js +0 -157
  137. package/yida-skills/skills/sls-log-workbench/SKILL.md +0 -134
  138. package/yida-skills/skills/sls-log-workbench/references/workbench-reference.md +0 -130
  139. package/yida-skills/skills/sls-log-workbench/sls-query.js +0 -295
@@ -0,0 +1,408 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { CliError } = require('./cli-error');
6
+
7
+ const TRUE_VALUES = new Set(['1', 'true', 'yes', 'y', 'on', 'schema-managed']);
8
+ const CONTEXT_FLAGS = new Set(['--schema-managed-context', '--resource-context', '--schema-context']);
9
+ const STATE_FLAGS = new Set(['--schema-state', '--schema-state-path']);
10
+
11
+ function extractLegacyGuardArgs(rawArgs = []) {
12
+ const args = [];
13
+ const guardOptions = {
14
+ schemaManaged: false,
15
+ contextInputs: [],
16
+ statePaths: [],
17
+ };
18
+
19
+ for (let index = 0; index < rawArgs.length; index++) {
20
+ const arg = rawArgs[index];
21
+ if (arg === '--schema-managed') {
22
+ guardOptions.schemaManaged = true;
23
+ continue;
24
+ }
25
+ if (typeof arg === 'string' && arg.startsWith('--schema-managed=')) {
26
+ guardOptions.schemaManaged = isTruthy(arg.slice('--schema-managed='.length));
27
+ continue;
28
+ }
29
+ const inlineContext = readInlineValue(arg, CONTEXT_FLAGS);
30
+ if (inlineContext !== null) {
31
+ guardOptions.contextInputs.push(inlineContext);
32
+ continue;
33
+ }
34
+ if (CONTEXT_FLAGS.has(arg)) {
35
+ const value = rawArgs[index + 1];
36
+ if (!value || String(value).startsWith('--')) {
37
+ throw invalidGuardInput(`${arg} requires a JSON value or file path`);
38
+ }
39
+ guardOptions.contextInputs.push(value);
40
+ index += 1;
41
+ continue;
42
+ }
43
+ const inlineState = readInlineValue(arg, STATE_FLAGS);
44
+ if (inlineState !== null) {
45
+ guardOptions.statePaths.push(inlineState);
46
+ continue;
47
+ }
48
+ if (STATE_FLAGS.has(arg)) {
49
+ const value = rawArgs[index + 1];
50
+ if (!value || String(value).startsWith('--')) {
51
+ throw invalidGuardInput(`${arg} requires a state file path`);
52
+ }
53
+ guardOptions.statePaths.push(value);
54
+ index += 1;
55
+ continue;
56
+ }
57
+ args.push(arg);
58
+ }
59
+
60
+ return { args, guardOptions };
61
+ }
62
+
63
+ function assertLegacyDirectWriteAllowed(target = {}, options = {}) {
64
+ const env = options.env || process.env;
65
+ const fsImpl = options.fsImpl || fs;
66
+ const cwd = options.cwd || process.cwd();
67
+ const guardOptions = options.guardOptions || {};
68
+ const contexts = collectContexts(guardOptions, env, fsImpl, cwd);
69
+ const statePaths = collectStatePaths(guardOptions, env);
70
+ const reasons = [];
71
+
72
+ if (guardOptions.schemaManaged || isTruthy(env.OPENYIDA_SCHEMA_MANAGED)) {
73
+ reasons.push({ source: 'explicit_schema_managed_flag' });
74
+ }
75
+
76
+ for (const context of contexts) {
77
+ const managedReason = matchManagedContext(context, target);
78
+ if (managedReason) {
79
+ reasons.push(managedReason);
80
+ }
81
+ }
82
+
83
+ for (const statePath of statePaths) {
84
+ const stateReason = matchManagedStatePath(statePath, target, { fsImpl, cwd });
85
+ if (stateReason) {
86
+ reasons.push(stateReason);
87
+ }
88
+ }
89
+
90
+ if (reasons.length > 0) {
91
+ throw new CliError(
92
+ `Refusing legacy direct ${target.command || 'write'} because the target is schema-managed. Use openyida schema validate -> schema plan -> schema apply.`,
93
+ {
94
+ code: 'LEGACY_SCHEMA_MANAGED_GUARD',
95
+ details: sanitizeGuardDetails(target, reasons),
96
+ }
97
+ );
98
+ }
99
+
100
+ const conflict = findBoundResourceConflict(contexts, target);
101
+ if (conflict) {
102
+ throw new CliError(
103
+ `Refusing legacy direct ${target.command || 'create'} because resource context already contains a bound ${conflict.resourceType}. Reuse the bound resource or set allowCreate=true explicitly.`,
104
+ {
105
+ code: 'LEGACY_RESOURCE_CONTEXT_CONFLICT',
106
+ details: sanitizeGuardDetails(target, [conflict]),
107
+ }
108
+ );
109
+ }
110
+ }
111
+
112
+ function collectContexts(guardOptions, env, fsImpl, cwd) {
113
+ const inputs = [];
114
+ if (env.OPENYIDA_RESOURCE_CONTEXT) {
115
+ inputs.push(env.OPENYIDA_RESOURCE_CONTEXT);
116
+ }
117
+ if (env.OPENYIDA_SCHEMA_CONTEXT) {
118
+ inputs.push(env.OPENYIDA_SCHEMA_CONTEXT);
119
+ }
120
+ if (env.OPENYIDA_SCHEMA_MANAGED_CONTEXT) {
121
+ inputs.push(env.OPENYIDA_SCHEMA_MANAGED_CONTEXT);
122
+ }
123
+ if (Array.isArray(guardOptions.contextInputs)) {
124
+ inputs.push(...guardOptions.contextInputs);
125
+ }
126
+ return inputs.map(input => parseContextInput(input, { fsImpl, cwd }));
127
+ }
128
+
129
+ function collectStatePaths(guardOptions, env) {
130
+ const paths = [];
131
+ if (env.OPENYIDA_SCHEMA_STATE) {
132
+ paths.push(env.OPENYIDA_SCHEMA_STATE);
133
+ }
134
+ if (env.OPENYIDA_SCHEMA_STATE_PATH) {
135
+ paths.push(env.OPENYIDA_SCHEMA_STATE_PATH);
136
+ }
137
+ if (Array.isArray(guardOptions.statePaths)) {
138
+ paths.push(...guardOptions.statePaths);
139
+ }
140
+ return paths.filter(Boolean);
141
+ }
142
+
143
+ function parseContextInput(input, options = {}) {
144
+ if (input && typeof input === 'object' && !Array.isArray(input)) {
145
+ return input;
146
+ }
147
+ const raw = String(input || '').trim();
148
+ if (!raw) {
149
+ throw invalidGuardInput('Resource context is empty');
150
+ }
151
+ const fsImpl = options.fsImpl || fs;
152
+ const cwd = options.cwd || process.cwd();
153
+ if (raw.startsWith('{') || raw.startsWith('[')) {
154
+ return parseJson(raw, 'Resource context is not valid JSON');
155
+ }
156
+ const contextPath = path.resolve(cwd, raw);
157
+ if (!fsImpl.existsSync(contextPath)) {
158
+ throw invalidGuardInput(`Resource context file not found: ${raw}`);
159
+ }
160
+ return parseJson(fsImpl.readFileSync(contextPath, 'utf8'), `Resource context file is not valid JSON: ${raw}`);
161
+ }
162
+
163
+ function matchManagedContext(context, target) {
164
+ if (!context || typeof context !== 'object' || Array.isArray(context)) {
165
+ return null;
166
+ }
167
+ if (isManagedMarker(context)) {
168
+ return { source: 'schema_managed_context', resourceType: target.resourceType };
169
+ }
170
+ const candidates = collectContextResources(context, target.resourceType);
171
+ for (const candidate of candidates) {
172
+ if (isManagedMarker(candidate) && resourceMatchesTarget(candidate, target)) {
173
+ return {
174
+ source: 'schema_managed_context',
175
+ resourceType: target.resourceType,
176
+ key: candidate.key || candidate.name || candidate.source,
177
+ };
178
+ }
179
+ }
180
+ if (target.resourceType === 'process' && target.formUuid) {
181
+ for (const candidate of collectContextResources(context, 'form')) {
182
+ if (isManagedMarker(candidate) && resourceMatchesTarget(candidate, { resourceType: 'form', formUuid: target.formUuid })) {
183
+ return {
184
+ source: 'schema_managed_form_context',
185
+ resourceType: 'form',
186
+ key: candidate.key || candidate.name || candidate.source,
187
+ };
188
+ }
189
+ }
190
+ }
191
+ if (target.resourceType !== 'app' && target.appType) {
192
+ for (const candidate of collectContextResources(context, 'app')) {
193
+ if (isManagedMarker(candidate) && resourceMatchesTarget(candidate, { resourceType: 'app', appType: target.appType })) {
194
+ return {
195
+ source: 'schema_managed_app_context',
196
+ resourceType: 'app',
197
+ key: candidate.key || candidate.name || candidate.source,
198
+ };
199
+ }
200
+ }
201
+ }
202
+ return null;
203
+ }
204
+
205
+ function collectContextResources(context, resourceType) {
206
+ const values = [];
207
+ const direct = context[resourceType];
208
+ if (direct) {
209
+ values.push(direct);
210
+ }
211
+ if (resourceType === 'page' && context.pageContext) {
212
+ values.push(context.pageContext);
213
+ }
214
+ if (resourceType === 'form' && context.formContext) {
215
+ values.push(context.formContext);
216
+ }
217
+ if (Array.isArray(context.resources)) {
218
+ values.push(...context.resources.filter(item => !item.resourceType || item.resourceType === resourceType));
219
+ }
220
+ return values.filter(item => item && typeof item === 'object' && !Array.isArray(item));
221
+ }
222
+
223
+ function matchManagedStatePath(statePath, target, options = {}) {
224
+ const fsImpl = options.fsImpl || fs;
225
+ const cwd = options.cwd || process.cwd();
226
+ const resolved = path.resolve(cwd, statePath);
227
+ if (!fsImpl.existsSync(resolved)) {
228
+ throw invalidGuardInput(`Schema state file not found: ${statePath}`);
229
+ }
230
+ const state = parseJson(fsImpl.readFileSync(resolved, 'utf8'), `Schema state file is not valid JSON: ${statePath}`);
231
+ if (!state || state.kind !== 'openyida_resource_state' || !state.resources || typeof state.resources !== 'object') {
232
+ throw invalidGuardInput(`Schema state file is not an OpenYida resource state: ${statePath}`);
233
+ }
234
+ const directMatch = matchStateResourceCollection(state.resources[target.resourceType], target);
235
+ if (directMatch) {
236
+ return { source: 'schema_state', statePath, resourceType: target.resourceType, key: directMatch.key };
237
+ }
238
+ if (target.resourceType === 'process' && target.formUuid) {
239
+ const formMatch = matchStateResourceCollection(state.resources.form, { resourceType: 'form', formUuid: target.formUuid });
240
+ if (formMatch) {
241
+ return { source: 'schema_state_form', statePath, resourceType: 'form', key: formMatch.key };
242
+ }
243
+ }
244
+ if (target.appType) {
245
+ const appMatch = matchStateResourceCollection(state.resources.app, { resourceType: 'app', appType: target.appType });
246
+ if (appMatch) {
247
+ return { source: 'schema_state_app', statePath, resourceType: 'app', key: appMatch.key };
248
+ }
249
+ }
250
+ return null;
251
+ }
252
+
253
+ function matchStateResourceCollection(collection, target) {
254
+ if (!collection || typeof collection !== 'object') {
255
+ return null;
256
+ }
257
+ for (const [key, entry] of Object.entries(collection)) {
258
+ if (entry && typeof entry === 'object' && resourceMatchesTarget(entry.bindings || {}, target)) {
259
+ return { key };
260
+ }
261
+ }
262
+ return null;
263
+ }
264
+
265
+ function findBoundResourceConflict(contexts, target) {
266
+ if (target.action !== 'create') {
267
+ return null;
268
+ }
269
+ for (const context of contexts) {
270
+ for (const resourceType of getCreateConflictResourceTypes(target)) {
271
+ const candidates = collectContextResources(context, resourceType);
272
+ for (const candidate of candidates) {
273
+ if (candidate && candidate.allowCreate === false && hasBoundIdentity(candidate, resourceType)) {
274
+ return {
275
+ source: 'resource_context',
276
+ resourceType,
277
+ };
278
+ }
279
+ }
280
+ }
281
+ }
282
+ return null;
283
+ }
284
+
285
+ function getCreateConflictResourceTypes(target) {
286
+ if (target.resourceType === 'process') {
287
+ return ['process', 'form'];
288
+ }
289
+ return [target.resourceType];
290
+ }
291
+
292
+ function hasBoundIdentity(resource, resourceType) {
293
+ if (resourceType === 'app') {
294
+ return hasNonEmptyString(resource.appType);
295
+ }
296
+ if (resourceType === 'page' || resourceType === 'form') {
297
+ return hasNonEmptyString(resource.formUuid) || hasNonEmptyString(resource.pageId);
298
+ }
299
+ if (resourceType === 'process') {
300
+ return hasNonEmptyString(resource.processCode) || hasNonEmptyString(resource.formUuid);
301
+ }
302
+ return false;
303
+ }
304
+
305
+ function resourceMatchesTarget(resource, target) {
306
+ if (!resource || typeof resource !== 'object') {
307
+ return false;
308
+ }
309
+
310
+ if (target.resourceType === 'app') {
311
+ return hasNonEmptyString(target.appType) &&
312
+ hasNonEmptyString(resource.appType) &&
313
+ String(resource.appType) === String(target.appType);
314
+ }
315
+
316
+ if (target.resourceType === 'form' || target.resourceType === 'page') {
317
+ const targetFormUuid = target.formUuid || target.pageId;
318
+ const resourceFormUuid = resource.formUuid || resource.pageId;
319
+ return hasNonEmptyString(targetFormUuid) &&
320
+ hasNonEmptyString(resourceFormUuid) &&
321
+ String(resourceFormUuid) === String(targetFormUuid);
322
+ }
323
+
324
+ if (target.resourceType === 'process') {
325
+ if (hasNonEmptyString(target.processCode)) {
326
+ return hasNonEmptyString(resource.processCode) &&
327
+ String(resource.processCode) === String(target.processCode);
328
+ }
329
+ const targetFormUuid = target.formUuid || target.pageId;
330
+ const resourceFormUuid = resource.formUuid || resource.pageId;
331
+ return hasNonEmptyString(targetFormUuid) &&
332
+ hasNonEmptyString(resourceFormUuid) &&
333
+ String(resourceFormUuid) === String(targetFormUuid);
334
+ }
335
+
336
+ return false;
337
+ }
338
+
339
+ function isManagedMarker(value) {
340
+ if (!value || typeof value !== 'object') {
341
+ return false;
342
+ }
343
+ return value.schemaManaged === true ||
344
+ value.managed === true ||
345
+ value.executionPath === 'schema-managed' ||
346
+ value.path === 'schema-managed' ||
347
+ value.managedBy === 'schema' ||
348
+ value.source === 'schema_state' ||
349
+ value.source === 'schema_manifest' ||
350
+ value.kind === 'openyida_schema_managed_context';
351
+ }
352
+
353
+ function readInlineValue(arg, names) {
354
+ if (typeof arg !== 'string') {
355
+ return null;
356
+ }
357
+ for (const name of names) {
358
+ const prefix = `${name}=`;
359
+ if (arg.startsWith(prefix)) {
360
+ return arg.slice(prefix.length);
361
+ }
362
+ }
363
+ return null;
364
+ }
365
+
366
+ function parseJson(raw, message) {
367
+ try {
368
+ return JSON.parse(raw);
369
+ } catch (error) {
370
+ throw invalidGuardInput(message);
371
+ }
372
+ }
373
+
374
+ function invalidGuardInput(message) {
375
+ return new CliError(message, {
376
+ code: 'LEGACY_SCHEMA_GUARD_INVALID_CONTEXT',
377
+ });
378
+ }
379
+
380
+ function isTruthy(value) {
381
+ return TRUE_VALUES.has(String(value || '').trim().toLowerCase());
382
+ }
383
+
384
+ function hasNonEmptyString(value) {
385
+ return typeof value === 'string' && value.trim() !== '';
386
+ }
387
+
388
+ function sanitizeGuardDetails(target, reasons) {
389
+ return {
390
+ command: target.command,
391
+ resourceType: target.resourceType,
392
+ action: target.action,
393
+ appType: target.appType,
394
+ formUuid: target.formUuid,
395
+ processCode: target.processCode,
396
+ reasons,
397
+ };
398
+ }
399
+
400
+ module.exports = {
401
+ assertLegacyDirectWriteAllowed,
402
+ extractLegacyGuardArgs,
403
+ _private: {
404
+ matchManagedContext,
405
+ matchManagedStatePath,
406
+ resourceMatchesTarget,
407
+ },
408
+ };
@@ -43,6 +43,10 @@ module.exports = {
43
43
  cmd_publish: 'Compile and publish custom page',
44
44
  cmd_update_form_config: 'Update form configuration',
45
45
  cmd_get_form_config: 'Query form configuration',
46
+ group_schema: 'Schema-as-Code',
47
+ cmd_schema_validate: 'Validate Schema-as-Code manifest locally',
48
+ cmd_schema_plan: 'Plan Schema-as-Code changes with read-only remote observation',
49
+ cmd_schema_apply: 'Apply a reviewed Schema-as-Code plan with checkpoints and recovery guards',
46
50
  group_data: 'Data & Permissions',
47
51
  cmd_data: 'Unified data management (form/process/task/subform)',
48
52
  cmd_task_center: 'Global task center (todo/processed/cc etc.)',
@@ -127,7 +131,7 @@ Usage:
127
131
  Commands:
128
132
  env Detect AI tool environment and login status
129
133
  copy [--force] Copy project directory to current AI tool environment
130
- login Manage login credentials (cache first, then QR scan)
134
+ login Manage OAuth token credentials
131
135
  logout Logout / switch account
132
136
  create-app "<name>" [desc] [icon] [color] [theme] [nav] [layout] Create an app, output appType
133
137
  create-page <appType> "<pageName>" [--mode dashboard] Create a custom page, output pageId
@@ -285,6 +289,9 @@ Examples:
285
289
  save_permission_usage: 'Usage: openyida save-permission <appType> <formUuid> [--data-permission <json>] [--action-permission <json>]',
286
290
  save_permission_example: "Example: openyida save-permission APP_XXX FORM-XXX --data-permission '{\"role\":\"DEFAULT\",\"dataRange\":\"SELF\"}'",
287
291
  exec_failed: '\n❌ Execution failed: {0}',
292
+ login_usage: 'Usage: openyida login [entryUrl|--public|--alibaba|--intl] [--check-only] [--json] [--client-id <clientId>]',
293
+ login_example: 'Examples:\n openyida login # Open browser via OAuth loopback login\n openyida login --check-only --json # Check token auth status only\n openyida login --intl # Login against the international environment\n OPENYIDA_NO_BROWSER=1 openyida login # Only suppress auto-opening the browser; this is not a CLI login mode\n openyida auth login # Login alias',
294
+ login_unsupported_option: 'Removed legacy login option is no longer supported: {0}. Use token/OAuth login; host-injected mode only checks the injected token.',
288
295
  auth_usage: 'Usage: openyida auth <status|login|refresh|logout>',
289
296
  auth_example: 'Examples:\n openyida auth status # View login status\n openyida auth login # Perform login\n openyida auth refresh # Refresh login session\n openyida auth logout # Logout',
290
297
  org_usage: 'Usage: openyida org <list|switch> [--json] [--corp-id <corpId>]',
@@ -488,8 +495,6 @@ Examples:
488
495
  csrf_ok: ' ✅ csrf_token: {0}...',
489
496
  corp_id_ok: ' ✅ corpId: {0}',
490
497
  no_playwright: '\n❌ This version only supports token login. Run openyida login.',
491
- playwright_install1: ' npm install -g playwright',
492
- playwright_install2: ' npx playwright install chromium',
493
498
  browser_opening: '\n🔐 Opening browser for OAuth login...',
494
499
  login_url_label: ' Login URL: {0}',
495
500
  waiting_login: ' Waiting for login (up to 10 minutes)...',
@@ -1200,49 +1205,6 @@ Examples:
1200
1205
  example: 'Example: openyida publish pages/src/xxx.js APP_XXX FORM-XXX --health-check',
1201
1206
  },
1202
1207
 
1203
- // ── token-only disabled QR login ────────────────────────────────
1204
- qr_login: {
1205
- title: '🔐 Yida Terminal QR Code Login',
1206
- step_init: ' Step 1: Initializing session...',
1207
- step_get_qr: ' Step 2: Fetching QR code...',
1208
- scan_hint: ' 📱 Please scan the QR code below with DingTalk:',
1209
- browser_alternative: ' 💡 If this is not a cloud/remote machine, use browser login instead; if QR scan fails, try: {0}',
1210
- qr_url_label: ' QR code URL: {0}',
1211
- waiting_scan: ' ⏳ Waiting for scan (up to 2 minutes)...',
1212
- scanned_confirm: ' ✅ QR code scanned! Please confirm login on your phone...',
1213
- scan_success: ' ✅ Scan confirmed!',
1214
- step_exchange: ' Step 4: Exchanging login credentials...',
1215
- step_get_corps: ' Step 5: Fetching organization list...',
1216
- step_switch_corp: ' Step 7: Switching to selected organization...',
1217
- only_one_corp: ' ✅ Single organization detected: {0}, auto-selected',
1218
- select_corp_prompt: ' 🏢 Multiple organizations found, please select one:',
1219
- select_corp_input: ' Enter number (1-{0}): ',
1220
- select_corp_invalid: ' ❌ Invalid input, please enter a number between 1 and {0}',
1221
- target_corp_not_found: 'Specified corpId was not found in accessible organizations: {0}',
1222
- corp_selected: ' ✅ Selected organization: {0}',
1223
- login_success: '✅ Login successful!',
1224
- qrcode_fallback: ' ⚠️ qrcode package not installed, please visit the URL below manually:',
1225
- qrcode_render_failed: ' ⚠️ QR code render failed ({0}), please visit the URL below:',
1226
- get_qr_failed: 'Failed to parse QR code response: {0}',
1227
- get_qr_api_failed: 'QR code API failed: {0}',
1228
- get_qr_error: 'Failed to get QR code: {0}',
1229
- qr_expired: 'QR code expired, please login again',
1230
- poll_timeout: 'Scan timeout (2 minutes), please login again',
1231
- poll_error: 'Failed to poll scan status: {0}',
1232
- exchange_failed: 'Failed to parse auth code exchange response: {0}',
1233
- exchange_api_failed: 'Auth code exchange API failed: {0}',
1234
- exchange_error: 'Failed to exchange auth code: {0}',
1235
- get_corp_list_failed: 'Failed to parse organization list response: {0}',
1236
- get_corp_list_api_failed: 'Organization list API failed: {0}',
1237
- get_corps_warn: ' ⚠️ Failed to get organization list ({0}), using default organization',
1238
- switch_corp_failed: 'Failed to switch organization: {0}',
1239
- switch_corp_warn: ' ⚠️ Failed to switch organization ({0}), using current organization',
1240
- select_corp_warn: ' ⚠️ Organization selection failed ({0}), using default organization',
1241
- no_corp_available: 'No accessible organizations found',
1242
- no_csrf_in_cookie: 'Login succeeded but no csrf_token found, please try again',
1243
- stdin_closed: 'Input stream closed, cannot select organization',
1244
- },
1245
-
1246
1208
  // ── scripts/postinstall.js ─────────────────────────
1247
1209
  postinstall: {
1248
1210
  welcome_title: ' 🎉 Welcome to OpenYida! ',
@@ -43,6 +43,10 @@ module.exports = {
43
43
  cmd_publish: '编译并发布自定义页面',
44
44
  cmd_update_form_config: '更新表单配置',
45
45
  cmd_get_form_config: '查询表单配置',
46
+ group_schema: 'Schema-as-Code',
47
+ cmd_schema_validate: '本地校验 Schema-as-Code Manifest',
48
+ cmd_schema_plan: '只读观测远端并规划 Schema-as-Code 变更',
49
+ cmd_schema_apply: '使用 checkpoint 和恢复护栏执行已审阅的 Schema-as-Code 计划',
46
50
  group_data: '数据 & 权限',
47
51
  cmd_data: '统一数据管理(表单/流程/任务/子表单)',
48
52
  cmd_task_center: '全局任务中心(待办/已处理/抄送等)',
@@ -127,7 +131,7 @@ openyida - 宜搭命令行工具
127
131
  命令:
128
132
  env 检测当前 AI 工具环境和登录态
129
133
  copy [--force] 复制 project 工作目录到当前 AI 工具环境
130
- login 登录态管理(优先缓存,否则扫码)
134
+ login OAuth token 登录态管理
131
135
  logout 退出登录 / 切换账号
132
136
  create-app "<名称>" [描述] [图标] [颜色] [主题色] [导航风格] [布局] 创建应用,输出 appType
133
137
  create-page <appType> "<页面名>" [--mode dashboard] 创建自定义页面,输出 pageId
@@ -286,6 +290,9 @@ openyida - 宜搭命令行工具
286
290
  save_permission_usage: '用法: openyida save-permission <appType> <formUuid> [--data-permission <json>] [--action-permission <json>]',
287
291
  save_permission_example: "示例: openyida save-permission APP_XXX FORM-XXX --data-permission '{\"role\":\"DEFAULT\",\"dataRange\":\"SELF\"}'",
288
292
  exec_failed: '\n❌ 执行失败: {0}',
293
+ login_usage: '用法: openyida login [entryUrl|--public|--alibaba|--intl] [--check-only] [--json] [--client-id <clientId>]',
294
+ login_example: '示例:\n openyida login # 通过 OAuth loopback 打开浏览器登录\n openyida login --check-only --json # 只检查 token 登录态\n openyida login --intl # 使用国际站环境登录\n OPENYIDA_NO_BROWSER=1 openyida login # 仅抑制自动打开浏览器,不是 CLI 登录模式\n openyida auth login # 登录入口别名',
295
+ login_unsupported_option: '已删除的旧登录参数不再支持: {0}。请使用 token/OAuth 登录;host 注入模式下只检查宿主注入的 token。',
289
296
  first_run_title: ' 🤖 OpenYida - AI 问答模式已开启! ',
290
297
  first_run_welcome: ' {0}欢迎首次使用 OpenYida!{1} 以下是快速上手指南:',
291
298
  first_run_way1_title: ' 📝 方式一:直接描述需求',
@@ -460,8 +467,6 @@ openyida - 宜搭命令行工具
460
467
  csrf_ok: ' ✅ csrf_token: {0}...',
461
468
  corp_id_ok: ' ✅ corpId: {0}',
462
469
  no_playwright: '\n❌ 当前版本仅支持 token 登录,请运行 openyida login。',
463
- playwright_install1: ' npm install -g playwright',
464
- playwright_install2: ' npx playwright install chromium',
465
470
  browser_opening: '\n🔐 正在打开浏览器完成 OAuth 登录...',
466
471
  login_url_label: ' 登录地址: {0}',
467
472
  waiting_login: ' 等待登录完成(最长等待 10 分钟)...',
@@ -1194,49 +1199,6 @@ openyida - 宜搭命令行工具
1194
1199
  example: '示例:openyida publish pages/src/xxx.js APP_XXX FORM-XXX --health-check',
1195
1200
  },
1196
1201
 
1197
- // ── token-only disabled QR login ────────────────────────────────
1198
- qr_login: {
1199
- title: '🔐 宜搭终端二维码登录',
1200
- step_init: ' Step 1: 初始化会话...',
1201
- step_get_qr: ' Step 2: 获取二维码...',
1202
- scan_hint: ' 📱 请用钉钉扫描以下二维码登录:',
1203
- browser_alternative: ' 💡 如果当前不是云端/远程电脑,建议使用浏览器登录;扫码失败也请尝试:{0}',
1204
- qr_url_label: ' 二维码链接: {0}',
1205
- waiting_scan: ' ⏳ 等待扫码中(最长 2 分钟)...',
1206
- scanned_confirm: ' ✅ 已扫码!请在手机上确认登录...',
1207
- scan_success: ' ✅ 扫码确认成功!',
1208
- step_exchange: ' Step 4: 获取登录凭证...',
1209
- step_get_corps: ' Step 5: 获取组织列表...',
1210
- step_switch_corp: ' Step 7: 切换到目标组织...',
1211
- only_one_corp: ' ✅ 检测到唯一组织:{0},自动选择',
1212
- select_corp_prompt: ' 🏢 检测到多个可访问组织,请选择:',
1213
- select_corp_input: ' 请输入序号 (1-{0}): ',
1214
- select_corp_invalid: ' ❌ 无效输入,请输入 1 到 {0} 之间的数字',
1215
- target_corp_not_found: '未在可访问组织中找到指定 corpId:{0}',
1216
- corp_selected: ' ✅ 已选择组织:{0}',
1217
- login_success: '✅ 登录成功!',
1218
- qrcode_fallback: ' ⚠️ qrcode 包未安装,请手动访问以下链接完成登录:',
1219
- qrcode_render_failed: ' ⚠️ 二维码渲染失败({0}),请手动访问以下链接:',
1220
- get_qr_failed: '获取二维码响应解析失败: {0}',
1221
- get_qr_api_failed: '获取二维码接口失败: {0}',
1222
- get_qr_error: '获取二维码失败: {0}',
1223
- qr_expired: '二维码已过期,请重新登录',
1224
- poll_timeout: '等待扫码超时(2 分钟),请重新登录',
1225
- poll_error: '轮询扫码状态失败: {0}',
1226
- exchange_failed: '换取登录凭证响应解析失败: {0}',
1227
- exchange_api_failed: '换取登录凭证接口失败: {0}',
1228
- exchange_error: '换取登录凭证失败: {0}',
1229
- get_corp_list_failed: '获取组织列表响应解析失败: {0}',
1230
- get_corp_list_api_failed: '获取组织列表接口失败: {0}',
1231
- get_corps_warn: ' ⚠️ 获取组织列表失败({0}),将使用默认组织',
1232
- switch_corp_failed: '切换组织失败: {0}',
1233
- switch_corp_warn: ' ⚠️ 切换组织失败({0}),将使用当前组织',
1234
- select_corp_warn: ' ⚠️ 组织选择失败({0}),将使用默认组织',
1235
- no_corp_available: '未找到可访问的组织',
1236
- no_csrf_in_cookie: '登录成功但未获取到 csrf_token,请重试',
1237
- stdin_closed: '输入流已关闭,无法选择组织',
1238
- },
1239
-
1240
1202
  // ── scripts/postinstall.js ─────────────────────────
1241
1203
  postinstall: {
1242
1204
  welcome_title: ' 🎉 欢迎使用 OpenYida! ',
@@ -171,7 +171,7 @@ function applyTemplateVariables(content, variables) {
171
171
  */
172
172
  async function run(args) {
173
173
  // --list 模式
174
- if (args.includes('--list') || args.length === 0) {
174
+ if (args.includes('--list') || args.includes('--help') || args.includes('-h') || args.length === 0) {
175
175
  printSampleList();
176
176
  return;
177
177
  }