@stalfh233/omc-cli 0.4.15 → 0.4.17

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 (185) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +297 -297
  3. package/coverage/m1-coverage-manifest-v1.json +1278 -1277
  4. package/dist/args.js +0 -0
  5. package/dist/artifacts.js +0 -0
  6. package/dist/bench/call-face-eval.js +0 -0
  7. package/dist/bench/context-attribution.js +0 -0
  8. package/dist/bench/discovery-cost-eval.js +0 -0
  9. package/dist/bench/driver.js +0 -0
  10. package/dist/bench/evals.js +0 -0
  11. package/dist/bench/fake-http-server.js +0 -0
  12. package/dist/bench/instrument.js +0 -0
  13. package/dist/bench/intent-face-eval.js +40 -40
  14. package/dist/bench/run.js +0 -0
  15. package/dist/bench/scenario.js +0 -0
  16. package/dist/bench/types.js +0 -0
  17. package/dist/bi-wire.js +0 -0
  18. package/dist/bizservice-config.js +38 -13
  19. package/dist/call.js +0 -0
  20. package/dist/capability-absences.js +0 -0
  21. package/dist/capability-overview.js +2 -2
  22. package/dist/capability-shape.js +0 -0
  23. package/dist/cli-contract.js +0 -0
  24. package/dist/cli-output.js +41 -15
  25. package/dist/cli.js +24 -2
  26. package/dist/code-rules.js +0 -0
  27. package/dist/command-transport.js +0 -0
  28. package/dist/config-store.js +4 -0
  29. package/dist/context.js +0 -0
  30. package/dist/contract-consistency.js +0 -0
  31. package/dist/contract-resources.js +0 -0
  32. package/dist/coverage-consistency.js +0 -0
  33. package/dist/coverage-registry.js +0 -0
  34. package/dist/coverage.js +0 -0
  35. package/dist/data-list-filter.js +0 -0
  36. package/dist/discovery.js +0 -0
  37. package/dist/endpoints.js +2 -0
  38. package/dist/environment-policy.js +0 -0
  39. package/dist/execution-metadata.js +4 -4
  40. package/dist/fake/app.js +0 -0
  41. package/dist/fake/b2-registration.js +26 -0
  42. package/dist/fake/businessrule.js +0 -0
  43. package/dist/fake/datarule.js +0 -0
  44. package/dist/fake/dictionary.js +0 -0
  45. package/dist/fake/environment.js +0 -0
  46. package/dist/fake/field.js +0 -0
  47. package/dist/fake/form.js +0 -0
  48. package/dist/fake/index.js +0 -0
  49. package/dist/fake/list-view.js +0 -0
  50. package/dist/fake/model.js +0 -0
  51. package/dist/fake/online-js.js +0 -0
  52. package/dist/fake/report.js +0 -0
  53. package/dist/fake/routes.js +0 -0
  54. package/dist/fake/rule-lifecycle.js +0 -0
  55. package/dist/fake/runtime-data.js +0 -0
  56. package/dist/fake/state.js +0 -0
  57. package/dist/fake/workflow.js +0 -0
  58. package/dist/field-change.js +0 -0
  59. package/dist/field-families.js +48 -0
  60. package/dist/form-layout.js +0 -0
  61. package/dist/form-support.js +12 -2
  62. package/dist/goal-routes.js +0 -0
  63. package/dist/governed-execution.js +0 -0
  64. package/dist/human-summary.js +0 -0
  65. package/dist/identity.js +0 -0
  66. package/dist/intent/baseline.js +0 -0
  67. package/dist/intent/capabilities/bizservice.js +0 -0
  68. package/dist/intent/capabilities/businessrule.js +0 -0
  69. package/dist/intent/capabilities/datarule.js +0 -0
  70. package/dist/intent/capabilities/field.js +0 -0
  71. package/dist/intent/capabilities/form.js +0 -0
  72. package/dist/intent/capabilities/index.js +0 -0
  73. package/dist/intent/capabilities/listview.js +0 -0
  74. package/dist/intent/capabilities/model.js +0 -0
  75. package/dist/intent/capabilities/onlinejs.js +0 -0
  76. package/dist/intent/capabilities/report.js +0 -0
  77. package/dist/intent/capabilities/workflow.js +0 -0
  78. package/dist/intent/capability.js +0 -0
  79. package/dist/intent/cli.js +0 -0
  80. package/dist/intent/compare.js +0 -0
  81. package/dist/intent/compiler.js +0 -0
  82. package/dist/intent/dsl.js +0 -0
  83. package/dist/intent/plan-file.js +0 -0
  84. package/dist/intent/readback.js +0 -0
  85. package/dist/intent/schema.js +0 -0
  86. package/dist/intent/validation.js +0 -0
  87. package/dist/intent/yaml.js +0 -0
  88. package/dist/json-column.js +0 -0
  89. package/dist/lanes/app-contract.js +0 -0
  90. package/dist/lanes/app-coverage.js +0 -0
  91. package/dist/lanes/app.js +0 -0
  92. package/dist/lanes/apply-changes.js +14 -1
  93. package/dist/lanes/b2-registration-contract.js +5 -0
  94. package/dist/lanes/b2-registration-coverage.js +1 -0
  95. package/dist/lanes/b2-registration.js +55 -6
  96. package/dist/lanes/businessrule-contract.js +0 -0
  97. package/dist/lanes/businessrule-coverage.js +0 -0
  98. package/dist/lanes/businessrule.js +0 -0
  99. package/dist/lanes/contract-support.js +0 -0
  100. package/dist/lanes/coverage-declaration.js +0 -0
  101. package/dist/lanes/datarule-contract.js +0 -0
  102. package/dist/lanes/datarule-coverage.js +0 -0
  103. package/dist/lanes/datarule-protocol.js +0 -0
  104. package/dist/lanes/datarule.js +0 -0
  105. package/dist/lanes/dictionary-contract.js +0 -0
  106. package/dist/lanes/dictionary-coverage.js +0 -0
  107. package/dist/lanes/dictionary.js +0 -0
  108. package/dist/lanes/environment-contract.js +0 -0
  109. package/dist/lanes/environment-coverage.js +0 -0
  110. package/dist/lanes/environment.js +0 -0
  111. package/dist/lanes/field-contract.js +3 -2
  112. package/dist/lanes/field-coverage.js +0 -0
  113. package/dist/lanes/field.js +268 -172
  114. package/dist/lanes/form-contract.js +2 -2
  115. package/dist/lanes/form-coverage.js +0 -0
  116. package/dist/lanes/form.js +4 -0
  117. package/dist/lanes/lane-ids.js +0 -0
  118. package/dist/lanes/list-view-contract.js +0 -0
  119. package/dist/lanes/list-view-coverage.js +0 -0
  120. package/dist/lanes/list-view-shapes.js +0 -0
  121. package/dist/lanes/list-view.js +0 -0
  122. package/dist/lanes/meta-contract.js +0 -0
  123. package/dist/lanes/meta.js +11 -1
  124. package/dist/lanes/model-contract.js +0 -0
  125. package/dist/lanes/model-coverage.js +0 -0
  126. package/dist/lanes/model.js +1 -1
  127. package/dist/lanes/online-js-contract.js +0 -0
  128. package/dist/lanes/online-js-coverage.js +0 -0
  129. package/dist/lanes/online-js.js +0 -0
  130. package/dist/lanes/report-contract.js +0 -0
  131. package/dist/lanes/report-coverage.js +0 -0
  132. package/dist/lanes/report.js +0 -0
  133. package/dist/lanes/rule-graph.js +0 -0
  134. package/dist/lanes/rule-lifecycle-contract.js +0 -0
  135. package/dist/lanes/rule-lifecycle-coverage.js +0 -0
  136. package/dist/lanes/rule-lifecycle.js +0 -0
  137. package/dist/lanes/runtime-data-contract.js +0 -0
  138. package/dist/lanes/runtime-data-coverage.js +0 -0
  139. package/dist/lanes/runtime-data.js +0 -0
  140. package/dist/lanes/workflow-contract.js +0 -0
  141. package/dist/lanes/workflow-coverage.js +0 -0
  142. package/dist/lanes/workflow.js +2 -0
  143. package/dist/omc-core-lazy.js +6 -0
  144. package/dist/online-js-layout.js +0 -0
  145. package/dist/online-js-source.js +28 -28
  146. package/dist/package-tool.js +0 -0
  147. package/dist/package.js +0 -0
  148. package/dist/plan.js +11 -1
  149. package/dist/read.js +0 -0
  150. package/dist/redact.js +0 -0
  151. package/dist/rule-support.js +0 -0
  152. package/dist/runtime-support.js +0 -0
  153. package/dist/service-doctor.js +28 -0
  154. package/dist/session-manager.js +14 -2
  155. package/dist/session.js +68 -13
  156. package/dist/skills.js +0 -0
  157. package/dist/sqlite-warning-filter.js +27 -0
  158. package/dist/support.js +0 -0
  159. package/dist/token-cache.js +65 -0
  160. package/dist/tool-types.js +0 -0
  161. package/dist/tools.js +0 -0
  162. package/dist/usage-log.js +4 -0
  163. package/dist/wire.js +0 -0
  164. package/dist/workflow-support.js +0 -0
  165. package/dist/write-lease.js +0 -0
  166. package/dist/write-lock.js +0 -0
  167. package/dist/write.js +24 -2
  168. package/dist/zip.js +0 -0
  169. package/docs/tool-surface-map.md +42 -42
  170. package/package.json +71 -71
  171. package/skills/omc-acceptance-criteria.md +47 -47
  172. package/skills/omc-business-configuration.md +258 -257
  173. package/skills/omc-capabilities.md +197 -197
  174. package/skills/omc-capability-scouting.md +55 -55
  175. package/skills/omc-config-draft-review.md +171 -171
  176. package/skills/omc-five-piece-flow.md +35 -35
  177. package/skills/omc-glossary.md +86 -86
  178. package/skills/omc-refusals.md +110 -110
  179. package/skills/omc-requirement-analysis.md +161 -161
  180. package/skills/omc-requirement-vocabulary.md +51 -51
  181. package/skills/omc-start-here.md +82 -82
  182. package/skills/omc-tool-selection.md +119 -119
  183. package/skills/omc-write-hazards.md +88 -87
  184. package/dist/approval-token.js +0 -102
  185. package/dist/server.js +0 -92
@@ -15,34 +15,34 @@ import { ToolRefusalError } from "./tool-types.js";
15
15
  * to replace it.
16
16
  */
17
17
  export const PLATFORM_LIFECYCLE_WRAPPER_MARKER = "(function(form)";
18
- export const PLATFORM_DEFAULT_LIFECYCLE_SCRIPT = `/**
19
- * 用户自定义JS区域;
20
- * 注:外部链接的JS统一放置在HTML中,原有script标签下的js统一放置在当前编码区域
21
- * 使用环境变量,可通过"$env("唤醒提示框,并通过$env(分类名称.变量名称)的方式使用
22
- */
23
- (function(form){
24
- /**
25
- * 事件绑定,form.on
26
- * @param eventType 事件类型
27
- * @params function 事件
28
- * @param cover true, false 是否覆盖root的生命周期 默认不覆盖
29
- */
30
- //数据加载后,渲染之前,this为window
31
- form.on('onLoad',function(data, dataPermission){},'cover');
32
- //渲染完成后
33
- form.on('onRendered',function(data){});
34
- //内置校验完成后,返回false阻止提交
35
- form.on('onValidate',function(action,data){});
36
- //操作前(包含自定义按钮)执行,返回false阻止按钮操作
37
- form.on('onPreAction',function(action,data){});
38
- //自定义按钮执行
39
- form.on('onCustomAction',function(action,data){});
40
- //操作完成后执行
41
- form.on('onActionDone',function(action,data,httpRes){});
42
- //表单字段打印数据格式扩展,可自行定义调用打印模板时,表单字段对应所打印数据的内容
43
- form.on('onPrintFormDataHandle',function(vm, printData, sourceData, formValues, key, locale, printTemplateCode, control, dataType, sheetData){});
44
- //流程日志打印数据格式扩展,可自行定义调用打印模板时,流程日志对应所打印数据的内容
45
- form.on('onPrintWorkflowLogDataHandle',function(vm, sourceData, printData, locale, printTemplateCode){});
18
+ export const PLATFORM_DEFAULT_LIFECYCLE_SCRIPT = `/**
19
+ * 用户自定义JS区域;
20
+ * 注:外部链接的JS统一放置在HTML中,原有script标签下的js统一放置在当前编码区域
21
+ * 使用环境变量,可通过"$env("唤醒提示框,并通过$env(分类名称.变量名称)的方式使用
22
+ */
23
+ (function(form){
24
+ /**
25
+ * 事件绑定,form.on
26
+ * @param eventType 事件类型
27
+ * @params function 事件
28
+ * @param cover true, false 是否覆盖root的生命周期 默认不覆盖
29
+ */
30
+ //数据加载后,渲染之前,this为window
31
+ form.on('onLoad',function(data, dataPermission){},'cover');
32
+ //渲染完成后
33
+ form.on('onRendered',function(data){});
34
+ //内置校验完成后,返回false阻止提交
35
+ form.on('onValidate',function(action,data){});
36
+ //操作前(包含自定义按钮)执行,返回false阻止按钮操作
37
+ form.on('onPreAction',function(action,data){});
38
+ //自定义按钮执行
39
+ form.on('onCustomAction',function(action,data){});
40
+ //操作完成后执行
41
+ form.on('onActionDone',function(action,data,httpRes){});
42
+ //表单字段打印数据格式扩展,可自行定义调用打印模板时,表单字段对应所打印数据的内容
43
+ form.on('onPrintFormDataHandle',function(vm, printData, sourceData, formValues, key, locale, printTemplateCode, control, dataType, sheetData){});
44
+ //流程日志打印数据格式扩展,可自行定义调用打印模板时,流程日志对应所打印数据的内容
45
+ form.on('onPrintWorkflowLogDataHandle',function(vm, sourceData, printData, locale, printTemplateCode){});
46
46
  })`;
47
47
  /** The lifecycle events the platform dispatches; anything else is a typo the runtime silently drops (or throws on). */
48
48
  export const ONLINE_JS_EVENTS = new Set([
File without changes
package/dist/package.js CHANGED
File without changes
package/dist/plan.js CHANGED
@@ -62,12 +62,22 @@ export async function planFor(ctx, environment, tool, args) {
62
62
  // The preview reads as a Chinese sentence produced by the SAME source as the
63
63
  // result summary (即将… / 已…), from the real arguments.
64
64
  const summary = writeSummary(tool, args, undefined, "plan");
65
+ // field.update cascadeRepublish: the plan previews the FULL cascade chain
66
+ // (the approver signs up for the form rebuild, not just the field write).
67
+ const actions = tool === "field.update" && args.cascadeRepublish === true
68
+ ? [
69
+ { tool, args: summarizedArgs(tool, args) },
70
+ { tool: "field.publish", args: { schemaCode: summarizeValue(args.schemaCode), code: summarizeValue(args.code), reason: "cascadeRepublish" } },
71
+ { tool: "form.draft", args: { schemaCode: summarizeValue(args.schemaCode), rebuild: true } },
72
+ { tool: "form.publish", args: { schemaCode: summarizeValue(args.schemaCode), force: true } },
73
+ ]
74
+ : [{ tool, args: summarizedArgs(tool, args) }];
65
75
  return {
66
76
  environment: session.environmentName,
67
77
  environmentFingerprint: session.environmentFingerprint(),
68
78
  summary,
69
79
  environmentNotice: environmentNotice(ctx.sessions.config().resolveEnvironment(environment).profile),
70
- actions: [{ tool, args: summarizedArgs(tool, args) }],
80
+ actions,
71
81
  };
72
82
  }
73
83
  //# sourceMappingURL=plan.js.map
package/dist/read.js CHANGED
File without changes
package/dist/redact.js CHANGED
File without changes
File without changes
File without changes
@@ -0,0 +1,28 @@
1
+ import { ENDPOINTS } from "./endpoints.js";
2
+ import { wireCall, WireError } from "./wire.js";
3
+ import { classifyAuthParameterlist } from "./bizservice-config.js";
4
+ import { decodeJsonColumn, JSON_COLUMN_PASSTHROUGH } from "./json-column.js";
5
+ export async function probeServiceCredentials(session, serviceCode) {
6
+ try {
7
+ // get_by_code answers a SINGLE row (not an array); an absent service
8
+ // answers the live 500001 业务服务不存在 business envelope.
9
+ const row = ((await wireCall(session, { path: ENDPOINTS.bizServiceGetByCode, query: { serviceCode } })).data ?? null);
10
+ if (row === null || Object.keys(row).length === 0)
11
+ return { status: "not-found", serviceCode };
12
+ const config = decodeJsonColumn(row.configJson, JSON_COLUMN_PASSTHROUGH);
13
+ const parameterlist = Array.isArray(config?.auth?.parameterlist) ? config.auth.parameterlist : [];
14
+ return {
15
+ status: "found",
16
+ serviceCode,
17
+ adapterCode: typeof row.adapterCode === "string" ? row.adapterCode : null,
18
+ parameterCount: parameterlist.length,
19
+ findings: classifyAuthParameterlist(parameterlist, String(row.adapterCode ?? "")),
20
+ };
21
+ }
22
+ catch (error) {
23
+ if (error instanceof WireError && error.kind === "business" && error.errcode === 500001)
24
+ return { status: "not-found", serviceCode };
25
+ return { status: "error", serviceCode, message: error instanceof Error ? error.message : String(error) };
26
+ }
27
+ }
28
+ //# sourceMappingURL=service-doctor.js.map
@@ -1,4 +1,5 @@
1
- import { CloudPivotSession } from "./session.js";
1
+ import { CloudPivotSession, defaultRequestTimeoutMs } from "./session.js";
2
+ import { TokenCache, loginFingerprint } from "./token-cache.js";
2
3
  /** Caches one logged-in session per named environment per process. */
3
4
  export class SessionManager {
4
5
  store;
@@ -20,7 +21,18 @@ export class SessionManager {
20
21
  let session = cached?.profileKey === profileKey ? cached.session : undefined;
21
22
  if (!session) {
22
23
  const credential = this.store.readCredential(resolved.name);
23
- session = new CloudPivotSession(resolved.name, resolved.profile, credential, this.httpFactory?.(resolved.profile), this.systemConfigFactory?.(resolved.profile));
24
+ // F1: cross-process token cache — lives beside the credentials, keyed
25
+ // by the environment profile + credential fingerprint. Injected http
26
+ // factories (tests, fakes) still go through the same hooks; a fake
27
+ // accepts the cached token the same way it accepts a fresh one.
28
+ const cache = new TokenCache(this.store.configPaths.configPath);
29
+ const environmentName = resolved.name;
30
+ const fingerprint = loginFingerprint(resolved.profile, credential);
31
+ session = new CloudPivotSession(resolved.name, resolved.profile, credential, this.httpFactory?.(resolved.profile), this.systemConfigFactory?.(resolved.profile), defaultRequestTimeoutMs(), {
32
+ load: () => cache.load(environmentName, fingerprint),
33
+ save: (login) => cache.save(environmentName, fingerprint, login),
34
+ clear: () => cache.clear(environmentName),
35
+ });
24
36
  this.sessions.set(resolved.name, { profileKey, session });
25
37
  }
26
38
  await session.login();
package/dist/session.js CHANGED
@@ -1,35 +1,77 @@
1
- import { CloudPivotAuthenticator, cloudPivotTenantHeaders, fetchCloudPivotHttp, resolveCloudPivotAuthoritativeOrigin, } from "omc-core";
2
- import { canonicalCloudPivotEnvironmentFingerprint } from "omc-core";
1
+ import { omcCore } from "./omc-core-lazy.js";
3
2
  /**
4
3
  * An authenticated CloudPivot session for one named environment. Wraps the
5
4
  * core authenticator; exposes a version-family probe used by the 8.6-only
6
5
  * fail-closed write gate, and the canonical environment fingerprint that all
7
6
  * plans/approvals/artifacts must carry.
7
+ *
8
+ * T04: omc-core loads lazily. Its modules statically import `node:sqlite`,
9
+ * whose ExperimentalWarning fires during the ESM LINK phase — before any user
10
+ * code in a static graph can install a stderr filter. Keeping omc-core out of
11
+ * the CLI's static graph lets `sqlite-warning-filter` (cli.ts first import)
12
+ * intercept the warning when the dynamic import links later. All omc-core
13
+ * values are captured during `login()`; the sync accessors stay sync.
8
14
  */
9
15
  export class CloudPivotSession {
10
16
  environmentName;
11
17
  profile;
12
18
  credential;
13
- http;
14
19
  systemConfig;
15
20
  requestTimeoutMs;
21
+ tokenCache;
16
22
  loginResult;
17
23
  version;
18
- constructor(environmentName, profile, credential, http = fetchCloudPivotHttp(profile.baseUrl, undefined, fetch, cloudPivotTenantHeaders(profile.engineCode)), systemConfig = undefined, requestTimeoutMs = defaultRequestTimeoutMs()) {
24
+ httpAdapter;
25
+ fingerprintOf;
26
+ constructor(environmentName, profile, credential, http, systemConfig = undefined, requestTimeoutMs = defaultRequestTimeoutMs(), tokenCache) {
19
27
  this.environmentName = environmentName;
20
28
  this.profile = profile;
21
29
  this.credential = credential;
22
- this.http = http;
23
30
  this.systemConfig = systemConfig;
24
31
  this.requestTimeoutMs = requestTimeoutMs;
32
+ this.tokenCache = tokenCache;
33
+ this.httpAdapter = http;
34
+ }
35
+ async http() {
36
+ if (this.httpAdapter)
37
+ return this.httpAdapter;
38
+ const core = await omcCore();
39
+ this.httpAdapter = core.fetchCloudPivotHttp(this.profile.baseUrl, undefined, fetch, core.cloudPivotTenantHeaders(this.profile.engineCode));
40
+ if (!this.fingerprintOf)
41
+ this.fingerprintOf = core.canonicalCloudPivotEnvironmentFingerprint;
42
+ return this.httpAdapter;
25
43
  }
26
44
  async login() {
27
45
  if (this.loginResult)
28
46
  return this.loginResult;
29
- const { systemConfig } = this.systemConfig
30
- ? { systemConfig: this.systemConfig }
31
- : await resolveCloudPivotAuthoritativeOrigin(this.profile.baseUrl, undefined, fetch, cloudPivotTenantHeaders(this.profile.engineCode)).then((r) => ({ systemConfig: r.systemConfig }), () => ({ systemConfig: undefined }));
32
- const authenticator = new CloudPivotAuthenticator(this.http);
47
+ // F1: a fresh-enough cached login (same environment profile + credential
48
+ // fingerprint) skips the six-round-trip handshake entirely. A 401
49
+ // mid-call clears the cache and re-logins (request() below).
50
+ const cached = this.tokenCache?.load();
51
+ if (cached) {
52
+ const core = await omcCore();
53
+ if (!this.fingerprintOf)
54
+ this.fingerprintOf = core.canonicalCloudPivotEnvironmentFingerprint;
55
+ this.loginResult = {
56
+ token: cached.token,
57
+ enterpriseId: cached.enterpriseId,
58
+ administrator: cached.administrator,
59
+ ...(cached.accountIdentity !== undefined ? { accountIdentity: cached.accountIdentity } : {}),
60
+ };
61
+ if (cached.version !== undefined)
62
+ this.version = cached.version;
63
+ return this.loginResult;
64
+ }
65
+ const core = await omcCore();
66
+ if (!this.fingerprintOf)
67
+ this.fingerprintOf = core.canonicalCloudPivotEnvironmentFingerprint;
68
+ let systemConfig = this.systemConfig;
69
+ if (!systemConfig) {
70
+ systemConfig = await core
71
+ .resolveCloudPivotAuthoritativeOrigin(this.profile.baseUrl, undefined, fetch, core.cloudPivotTenantHeaders(this.profile.engineCode))
72
+ .then((r) => r.systemConfig, () => undefined);
73
+ }
74
+ const authenticator = new core.CloudPivotAuthenticator(await this.http());
33
75
  this.loginResult = await authenticator.login({
34
76
  baseUrl: this.profile.baseUrl,
35
77
  username: this.credential.username,
@@ -37,6 +79,13 @@ export class CloudPivotSession {
37
79
  ...(systemConfig ? { systemConfig } : {}),
38
80
  });
39
81
  this.version = detectVersion(systemConfig);
82
+ this.tokenCache?.save({
83
+ token: this.loginResult.token,
84
+ enterpriseId: this.loginResult.enterpriseId,
85
+ administrator: this.loginResult.administrator,
86
+ ...(this.loginResult.accountIdentity !== undefined ? { accountIdentity: this.loginResult.accountIdentity } : {}),
87
+ ...(this.version !== undefined ? { version: this.version } : {}),
88
+ });
40
89
  return this.loginResult;
41
90
  }
42
91
  /** Version-family evidence; absent evidence returns undefined (fail closed upstream). */
@@ -64,7 +113,9 @@ export class CloudPivotSession {
64
113
  environmentFingerprint() {
65
114
  if (!this.loginResult)
66
115
  throw new Error("session is not logged in");
67
- return canonicalCloudPivotEnvironmentFingerprint({
116
+ if (!this.fingerprintOf)
117
+ throw new Error("session fingerprint fn not captured (login first)");
118
+ return this.fingerprintOf({
68
119
  environment: this.environmentName,
69
120
  authoritativeOrigin: new URL(this.profile.baseUrl).origin,
70
121
  applicationCode: this.profile.applicationCode,
@@ -80,6 +131,8 @@ export class CloudPivotSession {
80
131
  return first;
81
132
  this.loginResult = undefined;
82
133
  this.version = undefined;
134
+ // The invalidated token must not be re-loaded by the re-login below.
135
+ this.tokenCache?.clear();
83
136
  return this.send(input);
84
137
  }
85
138
  async send(input) {
@@ -88,7 +141,7 @@ export class CloudPivotSession {
88
141
  let timer;
89
142
  try {
90
143
  return await Promise.race([
91
- this.http.request({
144
+ (await this.http()).request({
92
145
  path: input.path,
93
146
  method: input.method,
94
147
  ...(input.query ? { query: input.query } : {}),
@@ -108,7 +161,9 @@ export class CloudPivotSession {
108
161
  }
109
162
  }
110
163
  get httpHandle() {
111
- return this.http;
164
+ if (!this.httpAdapter)
165
+ throw new Error("session http adapter not materialised (login first)");
166
+ return this.httpAdapter;
112
167
  }
113
168
  }
114
169
  function detectVersion(systemConfig) {
@@ -141,7 +196,7 @@ export class CloudPivotRequestTimeoutError extends Error {
141
196
  this.name = "CloudPivotRequestTimeoutError";
142
197
  }
143
198
  }
144
- function defaultRequestTimeoutMs() {
199
+ export function defaultRequestTimeoutMs() {
145
200
  const raw = process.env.OMC_HTTP_TIMEOUT_MS;
146
201
  const parsed = raw ? Number(raw) : NaN;
147
202
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 120_000;
package/dist/skills.js CHANGED
File without changes
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 0.4.16 T04 — suppress the `ExperimentalWarning: SQLite` stderr noise.
3
+ *
4
+ * omc-core statically imports `node:sqlite` (7 modules), so EVERY CLI start —
5
+ * including `--version` — prints the Node experimental-feature warning to
6
+ * stderr. Agents treat stderr noise as signal and quote it into reports.
7
+ *
8
+ * Empirically (Node 24): registering a `process.on('warning')` listener does
9
+ * NOT suppress the print; `node --no-warnings` works but requires caller-side
10
+ * flags we do not control. Pre-wrapping `process.stderr.write` BEFORE the
11
+ * sqlite module graph loads is the only zero-overhead, all-paths fix.
12
+ *
13
+ * This module must stay the FIRST import of cli.ts: ESM evaluates imports in
14
+ * order, so this wrapper installs before session.ts pulls omc-core.
15
+ * Only the SQLite-specific warning lines are dropped; everything else passes
16
+ * through untouched.
17
+ */
18
+ const originalWrite = process.stderr.write.bind(process.stderr);
19
+ const FILTER = /ExperimentalWarning: SQLite|^\(Use `node --trace-warnings/;
20
+ process.stderr.write = ((chunk, encodingOrCb, maybeCb) => {
21
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
22
+ if (FILTER.test(text))
23
+ return true;
24
+ return originalWrite(chunk, encodingOrCb, maybeCb);
25
+ });
26
+ export {};
27
+ //# sourceMappingURL=sqlite-warning-filter.js.map
package/dist/support.js CHANGED
File without changes
@@ -0,0 +1,65 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ export function loginFingerprint(profile, credential) {
5
+ return createHash("sha256")
6
+ .update(JSON.stringify({ baseUrl: profile.baseUrl, applicationCode: profile.applicationCode, engineCode: profile.engineCode ?? null, username: credential.username, password: credential.password }))
7
+ .digest("hex");
8
+ }
9
+ const DEFAULT_TTL_MS = 8 * 60 * 60 * 1000;
10
+ export class TokenCache {
11
+ dir;
12
+ ttlMs;
13
+ constructor(configPath, ttlMs = Number(process.env.OMC_TOKEN_TTL_MS) > 0 ? Number(process.env.OMC_TOKEN_TTL_MS) : DEFAULT_TTL_MS) {
14
+ this.dir = join(dirname(configPath), "token-cache");
15
+ this.ttlMs = ttlMs;
16
+ }
17
+ load(environmentName, fingerprint) {
18
+ const file = join(this.dir, `${environmentName}.json`);
19
+ if (!existsSync(file))
20
+ return undefined;
21
+ let parsed;
22
+ try {
23
+ parsed = JSON.parse(readFileSync(file, "utf8"));
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ if (parsed.fingerprint !== fingerprint)
29
+ return undefined;
30
+ if (typeof parsed.storedAt !== "number" || Date.now() - parsed.storedAt > this.ttlMs)
31
+ return undefined;
32
+ if (typeof parsed.token !== "string" || typeof parsed.enterpriseId !== "string" || typeof parsed.administrator !== "boolean")
33
+ return undefined;
34
+ return {
35
+ token: parsed.token,
36
+ enterpriseId: parsed.enterpriseId,
37
+ administrator: parsed.administrator,
38
+ ...(parsed.accountIdentity !== undefined ? { accountIdentity: parsed.accountIdentity } : {}),
39
+ ...(parsed.version !== undefined ? { version: parsed.version } : {}),
40
+ };
41
+ }
42
+ save(environmentName, fingerprint, login) {
43
+ try {
44
+ mkdirSync(this.dir, { recursive: true, mode: 0o700 });
45
+ const entry = { ...login, fingerprint, storedAt: Date.now() };
46
+ const file = join(this.dir, `${environmentName}.json`);
47
+ const tmp = `${file}.${process.pid}.tmp`;
48
+ writeFileSync(tmp, JSON.stringify(entry), { mode: 0o600 });
49
+ renameSync(tmp, file);
50
+ }
51
+ catch {
52
+ // The cache is an optimization, never a dependency: a failed write
53
+ // only means the next process logs in fresh.
54
+ }
55
+ }
56
+ clear(environmentName) {
57
+ try {
58
+ rmSync(join(this.dir, `${environmentName}.json`), { force: true });
59
+ }
60
+ catch {
61
+ // best effort
62
+ }
63
+ }
64
+ }
65
+ //# sourceMappingURL=token-cache.js.map
File without changes
package/dist/tools.js CHANGED
File without changes
package/dist/usage-log.js CHANGED
@@ -91,6 +91,9 @@ export function recordUsageOutput(value, stdoutBytes, fileBytes = 0) {
91
91
  pending.status = String(envelope.status ?? "unknown");
92
92
  if (typeof error?.code === "string")
93
93
  pending.errorCode = error.code;
94
+ // 0.4.16:requestId 落盘(信封瘦身把它从 stdout 移除后,排障关联的唯一落点)
95
+ if (typeof envelope.requestId === "string")
96
+ pending.requestId = envelope.requestId;
94
97
  }
95
98
  export function completeUsage(exitCode) {
96
99
  if (!pending)
@@ -115,6 +118,7 @@ export function completeUsage(exitCode) {
115
118
  status: current.status,
116
119
  exitCode,
117
120
  ...(current.errorCode ? { errorCode: current.errorCode } : {}),
121
+ ...(current.requestId ? { requestId: current.requestId } : {}),
118
122
  };
119
123
  appendFileSync(path, `${JSON.stringify(record)}\n`, "utf8");
120
124
  }
package/dist/wire.js CHANGED
File without changes
File without changes
File without changes
File without changes
package/dist/write.js CHANGED
@@ -12,6 +12,7 @@ function parseJsonOrUndefined(text) {
12
12
  }
13
13
  }
14
14
  import { admitGovernedWrite } from "./governed-execution.js";
15
+ import { isPackageStopOutcome } from "./package.js";
15
16
  import { writeSummary } from "./human-summary.js";
16
17
  import { planFor } from "./plan.js";
17
18
  import { isUncertainWireError, sha256, wireErrorKind, writeArtifact } from "./support.js";
@@ -72,7 +73,27 @@ async function governedWrite(ctx, tool, args, execute, dryRunPayload) {
72
73
  return { status: "refused", blocker: `wire-${error.kind}`, errcode: error.errcode, message: error.message, ...(error.diagnostic ? { diagnostic: error.diagnostic } : {}) };
73
74
  throw error;
74
75
  }
75
- const { performed, readback } = executed;
76
+ // A write tool that runs a step chain (field.update cascadeRepublish) may
77
+ // RETURN its stop outcome instead of throwing: shape it exactly like the
78
+ // package stop (status/steps/failedStep/envelope, STOPPED artifact) so both
79
+ // layers report a mid-chain stop the same way.
80
+ if (isPackageStopOutcome(executed)) {
81
+ const artifact = {
82
+ tool: tool.name,
83
+ ...(args.requestId ? { requestId: args.requestId, requestFingerprint } : {}),
84
+ environment: session.environmentName,
85
+ environmentFingerprint: session.environmentFingerprint(),
86
+ steps: executed.steps,
87
+ failedStep: executed.failedStep,
88
+ stop: executed.envelope,
89
+ ...(productionConfirmed ? { productionConfirmed: true } : {}),
90
+ deliveryState: "STOPPED",
91
+ recordedAt: new Date().toISOString(),
92
+ };
93
+ const artifactRef = writeArtifact(ctx, `${tool.name}-${randomUUID().slice(0, 8)}.json`, artifact);
94
+ return { status: executed.status, steps: executed.steps, failedStep: executed.failedStep, ...executed.envelope, ...(args.requestId ? { requestId: args.requestId } : {}), artifactFingerprint: artifactRef.detailSha256 };
95
+ }
96
+ const { performed, readback, steps } = executed;
76
97
  // Every production write echoes the explicit acknowledgement into the audit
77
98
  // trail, so the record can always answer "was this a confirmed production
78
99
  // write?".
@@ -83,12 +104,13 @@ async function governedWrite(ctx, tool, args, execute, dryRunPayload) {
83
104
  environmentFingerprint: session.environmentFingerprint(),
84
105
  performed,
85
106
  readback,
107
+ ...(steps !== undefined ? { steps } : {}),
86
108
  ...(productionConfirmed ? { productionConfirmed: true } : {}),
87
109
  deliveryState: "DELIVERED",
88
110
  recordedAt: new Date().toISOString(),
89
111
  };
90
112
  const artifactRef = writeArtifact(ctx, `${tool.name}-${randomUUID().slice(0, 8)}.json`, artifact);
91
- return { status: "ok", deliveryState: "DELIVERED", performed, readback, ...(args.requestId ? { requestId: args.requestId, idempotency: idempotencyDeclaration(tool.name) } : {}), ...(productionConfirmed ? { productionConfirmed: true } : {}), summary: writeSummary(tool.name, args, { performed, readback }, "done"), detailFile: artifactRef.detailFile, detailSha256: artifactRef.detailSha256, artifactFingerprint: artifactRef.detailSha256 };
113
+ return { status: "ok", deliveryState: "DELIVERED", performed, readback, ...(steps !== undefined ? { steps } : {}), ...(args.requestId ? { requestId: args.requestId, idempotency: idempotencyDeclaration(tool.name) } : {}), ...(productionConfirmed ? { productionConfirmed: true } : {}), summary: writeSummary(tool.name, args, { performed, readback }, "done"), detailFile: artifactRef.detailFile, detailSha256: artifactRef.detailSha256, artifactFingerprint: artifactRef.detailSha256 };
92
114
  });
93
115
  }
94
116
  function idempotencyDeclaration(tool) {
package/dist/zip.js CHANGED
File without changes
@@ -1,42 +1,42 @@
1
- # Tool Surface ↔ Coverage Manifest Correspondence
2
-
3
- Baseline: `coverage/m1-coverage-manifest-v1.json` (39 items, M1, 8.6-only).
4
- Every `executable` and `partial` item maps to at least one tool below;
5
- `blocked` / `static-only` / `unsupported` items are hard boundaries —
6
- requesting them refuses with the manifest citation (red-team tested).
7
-
8
- Methodology (how to operate the tools) is read with `omc help <topic>` — see
9
- `skills/`. The tool surface is the "what"; those topics are the "how".
10
-
11
- | # | Manifest item (lane) | Status | Tools that carry it |
12
- |---|---|---|---|
13
- | 1 | W1-list-view | partial | `listview.create`, `listview.delete` (forced defaults, header readback, exact absence) |
14
- | 3 | W5-S26-aggregation | partial | `field.create` with formula/EXPRESSION scope (same-model AGGREGATION semantics documented in the manifest entry; no separate wire face was live-proven) |
15
- | 10 | W7-S29-greenfield | executable | `workflow.create`, `workflow.delete` |
16
- | 11 | W7-S29-residual-OQs | partial | surfaced by `coverage.manifest` + `get_status` (residual OQs are documented unknowns — no tool may claim past them) |
17
- | 12 | S25-rule-lifecycle | executable | `rule.enable`, `rule.disable`, `rule.delete` (invoke via enable + effect readback) |
18
- | 12a | B1-businessrule | executable | `rule.list` (latest runtime versions with full business-field projection; agent filters client-side) |
19
- | 13 | S32-business-integration | executable | `service.register`, `service.get`, `service.delete` (RESTFUL face) |
20
- | 15 | M2-runtime-data (data-entry projection) | partial | `data.load` (projection boundary: `form/load` authoritative) |
21
- | 15a | M2-runtime-data (query/list) | executable | `data.list` (simplified filters -> queryCondition, pagination, field projection) |
22
- | 21 | field-families | executable | `field.create`, `field.publish`, `field.delete`, `field.list` |
23
- | 22 | field-families (numeric-logical/date-time) | partial | same field tools, family types constrained to the proven set |
24
- | 23 | model-management | executable | `model.create`, `model.update` (app-tree NODE rename patch: bizSchemaCode/id/name/parentId required, modelType refused — fixed at create), `model.setSummary`, `model.delete`, `model.deleteMany` (publish aspect = model-scoped publication via `field.publish`/`form.publish`, exactly the proven ladder; no separate model-publish wire call exists) |
25
- | 24 | form-publish-chain | executable | `form.draft`, `form.publish`, `form.get` (draft-first, explicit publishedHtmlJson, control-level layout readback) |
26
- | 25 | B1-businessrule | executable | `rule.save` (dead-state + B1.2 CRUD/conditional node graphs) |
27
- | 26 | C-workflow-template | executable | `workflow.create` (minimal template lifecycle) |
28
- | 27 | online-js (composer) | executable | `onlinejs.compose` (platform skeleton preserved, canonical customScript element with array attrs, publish + SHA-256 readback). The design-time 在线开发 face (`/api/api/programming` JAVA source) is authored via 二开扩展包 and exposes no CLI tool |
29
- | 28 | M2-runtime-data (write/readback) | executable | `data.save`, `data.load`, `data.delete`, `data.list` |
30
- | 29 | B2-registration | executable | `service.register` (+ method), `service.get`, `service.delete` |
31
- | 30 | environment-governance | executable | `discover_environment`, `environment.fingerprint`, `doctor`, `get_status` |
32
- | 31 | e2e-delivery | executable | the governed chain itself: write tools (DELIVERED + artifact) → read tools (VERIFIED) → delete tools with exact-absence proof (CLEANED) |
33
- | 32 | app-management | executable | `app.list`, `app.create`, `app.delete` (design-time application package lifecycle; production writes permanently refused) |
34
-
35
- High-level orchestrations compose the same lanes under one token:
36
- `configure_model` (23+21+24), `configure_form` (24), `configure_rule`
37
- (25+12), `discover_environment` (30).
38
-
39
- Blocked/static-only/unsupported boundaries (no tools, enforced by refusal):
40
- W2-permissions, W5-S26-cross-model, W6-S27-* (nested child tables, relation
41
- linkage, child-to-child), W6-S28-*, S32-custom-extension, boundary-unsupported
42
- (inline backend coding, app publish/environment release, OCR/ELECTRONIC_SIGN).
1
+ # Tool Surface ↔ Coverage Manifest Correspondence
2
+
3
+ Baseline: `coverage/m1-coverage-manifest-v1.json` (39 items, M1, 8.6-only).
4
+ Every `executable` and `partial` item maps to at least one tool below;
5
+ `blocked` / `static-only` / `unsupported` items are hard boundaries —
6
+ requesting them refuses with the manifest citation (red-team tested).
7
+
8
+ Methodology (how to operate the tools) is read with `omc help <topic>` — see
9
+ `skills/`. The tool surface is the "what"; those topics are the "how".
10
+
11
+ | # | Manifest item (lane) | Status | Tools that carry it |
12
+ |---|---|---|---|
13
+ | 1 | W1-list-view | partial | `listview.create`, `listview.delete` (forced defaults, header readback, exact absence) |
14
+ | 3 | W5-S26-aggregation | partial | `field.create` with formula/EXPRESSION scope (same-model AGGREGATION semantics documented in the manifest entry; no separate wire face was live-proven) |
15
+ | 10 | W7-S29-greenfield | executable | `workflow.create`, `workflow.delete` |
16
+ | 11 | W7-S29-residual-OQs | partial | surfaced by `coverage.manifest` + `get_status` (residual OQs are documented unknowns — no tool may claim past them) |
17
+ | 12 | S25-rule-lifecycle | executable | `rule.enable`, `rule.disable`, `rule.delete` (invoke via enable + effect readback) |
18
+ | 12a | B1-businessrule | executable | `rule.list` (latest runtime versions with full business-field projection; agent filters client-side) |
19
+ | 13 | S32-business-integration | executable | `service.register`, `service.get`, `service.delete` (RESTFUL face) |
20
+ | 15 | M2-runtime-data (data-entry projection) | partial | `data.load` (projection boundary: `form/load` authoritative) |
21
+ | 15a | M2-runtime-data (query/list) | executable | `data.list` (simplified filters -> queryCondition, pagination, field projection) |
22
+ | 21 | field-families | executable | `field.create`, `field.publish`, `field.delete`, `field.list` |
23
+ | 22 | field-families (numeric-logical/date-time) | partial | same field tools, family types constrained to the proven set |
24
+ | 23 | model-management | executable | `model.create`, `model.update` (app-tree NODE rename patch: bizSchemaCode/id/name/parentId required, modelType refused — fixed at create), `model.setSummary`, `model.delete`, `model.deleteMany` (publish aspect = model-scoped publication via `field.publish`/`form.publish`, exactly the proven ladder; no separate model-publish wire call exists) |
25
+ | 24 | form-publish-chain | executable | `form.draft`, `form.publish`, `form.get` (draft-first, explicit publishedHtmlJson, control-level layout readback) |
26
+ | 25 | B1-businessrule | executable | `rule.save` (dead-state + B1.2 CRUD/conditional node graphs) |
27
+ | 26 | C-workflow-template | executable | `workflow.create` (minimal template lifecycle) |
28
+ | 27 | online-js (composer) | executable | `onlinejs.compose` (platform skeleton preserved, canonical customScript element with array attrs, publish + SHA-256 readback). The design-time 在线开发 face (`/api/api/programming` JAVA source) is authored via 二开扩展包 and exposes no CLI tool |
29
+ | 28 | M2-runtime-data (write/readback) | executable | `data.save`, `data.load`, `data.delete`, `data.list` |
30
+ | 29 | B2-registration | executable | `service.register` (+ method), `service.get`, `service.delete` |
31
+ | 30 | environment-governance | executable | `discover_environment`, `environment.fingerprint`, `doctor`, `get_status` |
32
+ | 31 | e2e-delivery | executable | the governed chain itself: write tools (DELIVERED + artifact) → read tools (VERIFIED) → delete tools with exact-absence proof (CLEANED) |
33
+ | 32 | app-management | executable | `app.list`, `app.create`, `app.delete` (design-time application package lifecycle; production writes permanently refused) |
34
+
35
+ High-level orchestrations compose the same lanes under one token:
36
+ `configure_model` (23+21+24), `configure_form` (24), `configure_rule`
37
+ (25+12), `discover_environment` (30).
38
+
39
+ Blocked/static-only/unsupported boundaries (no tools, enforced by refusal):
40
+ W2-permissions, W5-S26-cross-model, W6-S27-* (nested child tables, relation
41
+ linkage, child-to-child), W6-S28-*, S32-custom-extension, boundary-unsupported
42
+ (inline backend coding, app publish/environment release, OCR/ELECTRONIC_SIGN).