@google-cloud/kcmd 0.0.0-stage → 0.1.0

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 (150) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +266 -2
  3. package/build/ts/kcmd/cli.js +19578 -0
  4. package/build/ts/kcmd/gcp/alloydb.d.ts +12 -0
  5. package/build/ts/kcmd/gcp/alloydb.js +70 -0
  6. package/build/ts/kcmd/gcp/api.d.ts +29 -0
  7. package/build/ts/kcmd/gcp/api.js +162 -0
  8. package/build/ts/kcmd/gcp/biglake.d.ts +16 -0
  9. package/build/ts/kcmd/gcp/biglake.js +50 -0
  10. package/build/ts/kcmd/gcp/bigquery.d.ts +57 -0
  11. package/build/ts/kcmd/gcp/bigquery.js +134 -0
  12. package/build/ts/kcmd/gcp/context.d.ts +10 -0
  13. package/build/ts/kcmd/gcp/context.js +74 -0
  14. package/build/ts/kcmd/gcp/crm.d.ts +13 -0
  15. package/build/ts/kcmd/gcp/crm.js +91 -0
  16. package/build/ts/kcmd/gcp/dataplex.d.ts +147 -0
  17. package/build/ts/kcmd/gcp/dataplex.js +406 -0
  18. package/build/ts/kcmd/gcp/index.d.ts +3 -0
  19. package/build/ts/kcmd/gcp/index.js +21 -0
  20. package/build/ts/kcmd/gcp/spanner.d.ts +63 -0
  21. package/build/ts/kcmd/gcp/spanner.js +123 -0
  22. package/build/ts/kcmd/index.d.ts +7 -0
  23. package/build/ts/kcmd/index.js +46 -0
  24. package/build/ts/kcmd/layout.d.ts +16 -0
  25. package/build/ts/kcmd/layout.js +31 -0
  26. package/build/ts/kcmd/layouts/documents.d.ts +24 -0
  27. package/build/ts/kcmd/layouts/documents.js +264 -0
  28. package/build/ts/kcmd/layouts/okf.d.ts +31 -0
  29. package/build/ts/kcmd/layouts/okf.js +344 -0
  30. package/build/ts/kcmd/layouts/semantic-model.d.ts +61 -0
  31. package/build/ts/kcmd/layouts/semantic-model.js +542 -0
  32. package/build/ts/kcmd/layouts/standard.d.ts +13 -0
  33. package/build/ts/kcmd/layouts/standard.js +108 -0
  34. package/build/ts/kcmd/manifest.d.ts +34 -0
  35. package/build/ts/kcmd/manifest.js +262 -0
  36. package/build/ts/kcmd/metadata.d.ts +25 -0
  37. package/build/ts/kcmd/metadata.js +8 -0
  38. package/build/ts/kcmd/okf/catalog_residue.d.ts +14 -0
  39. package/build/ts/kcmd/okf/catalog_residue.js +124 -0
  40. package/build/ts/kcmd/okf/concept.d.ts +14 -0
  41. package/build/ts/kcmd/okf/concept.js +104 -0
  42. package/build/ts/kcmd/okf/concept_to_entry.d.ts +8 -0
  43. package/build/ts/kcmd/okf/concept_to_entry.js +254 -0
  44. package/build/ts/kcmd/okf/init_okf_workspace.d.ts +15 -0
  45. package/build/ts/kcmd/okf/init_okf_workspace.js +115 -0
  46. package/build/ts/kcmd/okf/links.d.ts +23 -0
  47. package/build/ts/kcmd/okf/links.js +468 -0
  48. package/build/ts/kcmd/okf/okf_metadata.d.ts +12 -0
  49. package/build/ts/kcmd/okf/okf_metadata.js +82 -0
  50. package/build/ts/kcmd/semantic/bigquery.d.ts +22 -0
  51. package/build/ts/kcmd/semantic/bigquery.js +525 -0
  52. package/build/ts/kcmd/semantic/binding.d.ts +3 -0
  53. package/build/ts/kcmd/semantic/binding.js +97 -0
  54. package/build/ts/kcmd/semantic/catalog_entry.d.ts +24 -0
  55. package/build/ts/kcmd/semantic/catalog_entry.js +35 -0
  56. package/build/ts/kcmd/semantic/converters/owl/convert.d.ts +26 -0
  57. package/build/ts/kcmd/semantic/converters/owl/convert.js +41 -0
  58. package/build/ts/kcmd/semantic/converters/owl/model.d.ts +98 -0
  59. package/build/ts/kcmd/semantic/converters/owl/model.js +30 -0
  60. package/build/ts/kcmd/semantic/converters/owl/parse.d.ts +11 -0
  61. package/build/ts/kcmd/semantic/converters/owl/parse.js +634 -0
  62. package/build/ts/kcmd/semantic/converters/owl/to_ir.d.ts +19 -0
  63. package/build/ts/kcmd/semantic/converters/owl/to_ir.js +390 -0
  64. package/build/ts/kcmd/semantic/deploy_bigquery.d.ts +20 -0
  65. package/build/ts/kcmd/semantic/deploy_bigquery.js +226 -0
  66. package/build/ts/kcmd/semantic/deploy_knowledge_catalog.d.ts +129 -0
  67. package/build/ts/kcmd/semantic/deploy_knowledge_catalog.js +1396 -0
  68. package/build/ts/kcmd/semantic/deploy_spanner.d.ts +15 -0
  69. package/build/ts/kcmd/semantic/deploy_spanner.js +150 -0
  70. package/build/ts/kcmd/semantic/deployment_target.d.ts +93 -0
  71. package/build/ts/kcmd/semantic/deployment_target.js +379 -0
  72. package/build/ts/kcmd/semantic/graph_ddl.d.ts +34 -0
  73. package/build/ts/kcmd/semantic/graph_ddl.js +563 -0
  74. package/build/ts/kcmd/semantic/import_model.d.ts +50 -0
  75. package/build/ts/kcmd/semantic/import_model.js +1275 -0
  76. package/build/ts/kcmd/semantic/ir.d.ts +589 -0
  77. package/build/ts/kcmd/semantic/ir.js +247 -0
  78. package/build/ts/kcmd/semantic/kc_actions.d.ts +45 -0
  79. package/build/ts/kcmd/semantic/kc_actions.js +531 -0
  80. package/build/ts/kcmd/semantic/kc_constraints.d.ts +44 -0
  81. package/build/ts/kcmd/semantic/kc_constraints.js +279 -0
  82. package/build/ts/kcmd/semantic/kc_converter.d.ts +31 -0
  83. package/build/ts/kcmd/semantic/kc_converter.js +824 -0
  84. package/build/ts/kcmd/semantic/kc_custom_types.d.ts +70 -0
  85. package/build/ts/kcmd/semantic/kc_custom_types.js +828 -0
  86. package/build/ts/kcmd/semantic/kc_entries.d.ts +155 -0
  87. package/build/ts/kcmd/semantic/kc_entries.js +284 -0
  88. package/build/ts/kcmd/semantic/kc_ids.d.ts +103 -0
  89. package/build/ts/kcmd/semantic/kc_ids.js +191 -0
  90. package/build/ts/kcmd/semantic/knowledge_catalog.d.ts +32 -0
  91. package/build/ts/kcmd/semantic/knowledge_catalog.js +1043 -0
  92. package/build/ts/kcmd/semantic/loader.d.ts +262 -0
  93. package/build/ts/kcmd/semantic/loader.js +3440 -0
  94. package/build/ts/kcmd/semantic/names.d.ts +10 -0
  95. package/build/ts/kcmd/semantic/names.js +85 -0
  96. package/build/ts/kcmd/semantic/osi_converter.d.ts +60 -0
  97. package/build/ts/kcmd/semantic/osi_converter.js +911 -0
  98. package/build/ts/kcmd/semantic/parameters.d.ts +18 -0
  99. package/build/ts/kcmd/semantic/parameters.js +152 -0
  100. package/build/ts/kcmd/semantic/pull_kc.d.ts +14 -0
  101. package/build/ts/kcmd/semantic/pull_kc.js +346 -0
  102. package/build/ts/kcmd/semantic/resolve_inheritance.d.ts +54 -0
  103. package/build/ts/kcmd/semantic/resolve_inheritance.js +330 -0
  104. package/build/ts/kcmd/semantic/resolve_profiles.d.ts +250 -0
  105. package/build/ts/kcmd/semantic/resolve_profiles.js +1822 -0
  106. package/build/ts/kcmd/semantic/runtime/agent_tools.d.ts +93 -0
  107. package/build/ts/kcmd/semantic/runtime/agent_tools.js +243 -0
  108. package/build/ts/kcmd/semantic/runtime/dialect.d.ts +13 -0
  109. package/build/ts/kcmd/semantic/runtime/dialect.js +39 -0
  110. package/build/ts/kcmd/semantic/runtime/runtime.d.ts +94 -0
  111. package/build/ts/kcmd/semantic/runtime/runtime.js +318 -0
  112. package/build/ts/kcmd/semantic/runtime/store.d.ts +112 -0
  113. package/build/ts/kcmd/semantic/runtime/store.js +344 -0
  114. package/build/ts/kcmd/semantic/skills.d.ts +110 -0
  115. package/build/ts/kcmd/semantic/skills.js +697 -0
  116. package/build/ts/kcmd/semantic/source_dialect.d.ts +10 -0
  117. package/build/ts/kcmd/semantic/source_dialect.js +261 -0
  118. package/build/ts/kcmd/semantic/source_entries.d.ts +12 -0
  119. package/build/ts/kcmd/semantic/source_entries.js +282 -0
  120. package/build/ts/kcmd/semantic/spanner.d.ts +18 -0
  121. package/build/ts/kcmd/semantic/spanner.js +60 -0
  122. package/build/ts/kcmd/semantic/sql_expr_utils.d.ts +17 -0
  123. package/build/ts/kcmd/semantic/sql_expr_utils.js +221 -0
  124. package/build/ts/kcmd/semantic/sql_identifiers.d.ts +6 -0
  125. package/build/ts/kcmd/semantic/sql_identifiers.js +267 -0
  126. package/build/ts/kcmd/semantic/sql_parser.d.ts +44 -0
  127. package/build/ts/kcmd/semantic/sql_parser.js +378 -0
  128. package/build/ts/kcmd/semantic/transpile.d.ts +74 -0
  129. package/build/ts/kcmd/semantic/transpile.js +563 -0
  130. package/build/ts/kcmd/semantic/validate.d.ts +35 -0
  131. package/build/ts/kcmd/semantic/validate.js +1899 -0
  132. package/build/ts/kcmd/semantic/yaml_options.d.ts +6 -0
  133. package/build/ts/kcmd/semantic/yaml_options.js +58 -0
  134. package/build/ts/kcmd/semantic/yaml_update.d.ts +26 -0
  135. package/build/ts/kcmd/semantic/yaml_update.js +782 -0
  136. package/build/ts/kcmd/snapshot.d.ts +33 -0
  137. package/build/ts/kcmd/snapshot.js +379 -0
  138. package/build/ts/kcmd/source.d.ts +31 -0
  139. package/build/ts/kcmd/source.js +119 -0
  140. package/build/ts/kcmd/sources/bq-dataset.d.ts +17 -0
  141. package/build/ts/kcmd/sources/bq-dataset.js +131 -0
  142. package/build/ts/kcmd/sources/entrygroup.d.ts +21 -0
  143. package/build/ts/kcmd/sources/entrygroup.js +83 -0
  144. package/build/ts/kcmd/sources/kb.d.ts +21 -0
  145. package/build/ts/kcmd/sources/kb.js +82 -0
  146. package/build/ts/kcmd/sources/semantic-model.d.ts +18 -0
  147. package/build/ts/kcmd/sources/semantic-model.js +48 -0
  148. package/build/ts/kcmd/sync.d.ts +35 -0
  149. package/build/ts/kcmd/sync.js +697 -0
  150. package/package.json +62 -4
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Deriving action tools and schema metadata for a bound semantic model.
3
+ *
4
+ * An action declares a name, a description of what it does, its typed
5
+ * parameters, the guidance an AI caller should follow
6
+ * (`ai_context.instructions`), and the rules that gate it (`guards`). This
7
+ * module resolves those declarations against the active binding profile so
8
+ * `kcmd skills-generate` can render the model as an Agent Skill.
9
+ */
10
+ import { Entity } from '../ir';
11
+ import { SqlDialect } from './dialect';
12
+ import { SemanticRuntime } from './runtime';
13
+ /** The JSON types a tool parameter can take. */
14
+ export type ToolParameterType = 'string' | 'number' | 'integer' | 'boolean';
15
+ /** One argument a tool accepts, derived from an action parameter. */
16
+ export interface ToolParameter {
17
+ name: string;
18
+ type: ToolParameterType;
19
+ /** What to pass, in the words the model's own types justify. */
20
+ description: string;
21
+ /**
22
+ * Whether the caller must supply this argument. An action parameter is
23
+ * required unless declared `required: false` or given a `default`.
24
+ */
25
+ required: boolean;
26
+ /**
27
+ * The default value substituted when the caller omits the argument, if any.
28
+ */
29
+ default?: unknown;
30
+ }
31
+ /** One tool, derived from one action. */
32
+ export interface ActionTool {
33
+ /** The action name in snake_case, which is what tool APIs expect. */
34
+ name: string;
35
+ /** The action this tool calls, by its authored name. */
36
+ actionName: string;
37
+ parameters: ToolParameter[];
38
+ /**
39
+ * Whether this action can run under the active binding profile. False when
40
+ * the profile supplies no executor, when a `sql` executor has no operational
41
+ * store, or when the action names a guard that is undeclared or states no
42
+ * rule text.
43
+ */
44
+ runnable: boolean;
45
+ /** Why `runnable` is false, in words a caller can report. */
46
+ unavailable?: string;
47
+ }
48
+ export interface ActionToolOptions {
49
+ /** The model to derive tools from, and the store they would run against. */
50
+ runtime: SemanticRuntime;
51
+ }
52
+ /**
53
+ * One tool per action the model declares, in declaration order.
54
+ *
55
+ * A model with no actions yields no tools, which is the honest answer: an
56
+ * agent over a read-only model has nothing to call.
57
+ */
58
+ export declare function actionTools(opts: ActionToolOptions): ActionTool[];
59
+ /** Everything a model offers an agent, in one name space. */
60
+ export interface ModelTools {
61
+ /** One write per action. */
62
+ actions: ActionTool[];
63
+ /**
64
+ * What to tell an agent holding these tools: the model's own
65
+ * `ai_context.instructions` followed by how the tools are meant to be used.
66
+ */
67
+ instruction: string;
68
+ }
69
+ /**
70
+ * Every tool a model offers, with the names guaranteed distinct.
71
+ */
72
+ export declare function modelTools(opts: ActionToolOptions): ModelTools;
73
+ interface BoundField {
74
+ name: string;
75
+ type: string;
76
+ column: string;
77
+ /** What the model says this field holds, if it says anything. */
78
+ description?: string;
79
+ }
80
+ /**
81
+ * An entity a statement can name: one table, and the columns behind it.
82
+ */
83
+ export interface ReadableEntity {
84
+ entity: Entity;
85
+ table: string;
86
+ fields: BoundField[];
87
+ }
88
+ /**
89
+ * What there is to read under this runtime: one entry per entity the model
90
+ * declares, the profile binds to a table, and a statement can name.
91
+ */
92
+ export declare function readableEntities(runtime: SemanticRuntime, dialect: SqlDialect): ReadableEntity[];
93
+ export {};
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ /**
3
+ * Deriving action tools and schema metadata for a bound semantic model.
4
+ *
5
+ * An action declares a name, a description of what it does, its typed
6
+ * parameters, the guidance an AI caller should follow
7
+ * (`ai_context.instructions`), and the rules that gate it (`guards`). This
8
+ * module resolves those declarations against the active binding profile so
9
+ * `kcmd skills-generate` can render the model as an Agent Skill.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.actionTools = actionTools;
13
+ exports.modelTools = modelTools;
14
+ exports.readableEntities = readableEntities;
15
+ const binding_1 = require("../binding");
16
+ const ir_1 = require("../ir");
17
+ const sql_expr_utils_1 = require("../sql_expr_utils");
18
+ const runtime_1 = require("./runtime");
19
+ /**
20
+ * One tool per action the model declares, in declaration order.
21
+ *
22
+ * A model with no actions yields no tools, which is the honest answer: an
23
+ * agent over a read-only model has nothing to call.
24
+ */
25
+ function actionTools(opts) {
26
+ return (opts.runtime.model.actions ?? [])
27
+ .map(action => toolFor(action, opts));
28
+ }
29
+ function toolFor(action, opts) {
30
+ const blocked = whyRefusedWithoutRunning(opts.runtime, action) ?? undefined;
31
+ const tool = {
32
+ name: snakeCase(action.name),
33
+ actionName: action.name,
34
+ parameters: action.parameters.map(toolParameter),
35
+ runnable: !blocked,
36
+ };
37
+ if (blocked)
38
+ tool.unavailable = blocked;
39
+ return tool;
40
+ }
41
+ function whyRefusedWithoutRunning(runtime, action) {
42
+ const model = runtime.model;
43
+ const executor = action.executor;
44
+ if (!executor) {
45
+ return `Action '${action.name}' has no executor under this binding, so ` +
46
+ `there is nothing to run. An executor is a physical binding: a ` +
47
+ `profile supplies one, and a profile that writes 'executor: null' ` +
48
+ `withdraws it. The action is still declared and still published; it ` +
49
+ `is only not performable here, and is performed somewhere else.`;
50
+ }
51
+ const advisory = new Set((model.constraints ?? [])
52
+ .filter(c => c.onViolation === 'warn')
53
+ .map(c => c.name));
54
+ const guards = (action.guards ?? []).filter(g => !advisory.has(g));
55
+ const blank = (model.constraints ?? [])
56
+ .filter(c => guards.includes(c.name) && !(c.judgment ?? '').trim())
57
+ .map(c => c.name);
58
+ if (blank.length) {
59
+ const says = blank.length === 1 ? 'states no rule to put to a judge' :
60
+ 'state no rule to put to a judge';
61
+ return `Action '${action.name}' is guarded by ${quoteList(blank)}, ` +
62
+ `which ${says}. There is nothing to put to a judge, so the action ` +
63
+ `is refused rather than run unchecked.`;
64
+ }
65
+ const declared = new Set((model.constraints ?? []).map(c => c.name));
66
+ const undeclared = guards.filter(g => !declared.has(g));
67
+ if (undeclared.length) {
68
+ return `Action '${action.name}' is guarded by ${quoteList(undeclared)}, ` +
69
+ `which ${undeclared.length === 1 ? 'is' : 'are'} not declared by ` +
70
+ `model '${model.name}'. Running it would apply a write the model says ` +
71
+ `must be checked first, so it is refused rather than run unchecked.`;
72
+ }
73
+ if (executor.kind === 'sql') {
74
+ return (0, runtime_1.runtimeStoreError)(runtime);
75
+ }
76
+ return null;
77
+ }
78
+ function quoteList(names) {
79
+ const quoted = names.map(n => `'${n}'`);
80
+ if (quoted.length === 1)
81
+ return quoted[0];
82
+ return `${quoted.slice(0, -1).join(', ')} and ${quoted[quoted.length - 1]}`;
83
+ }
84
+ function isParameterRequired(param) {
85
+ if (param.default !== undefined)
86
+ return false;
87
+ return param.required !== false;
88
+ }
89
+ function sentence(text) {
90
+ const trimmed = text.trim();
91
+ if (!trimmed)
92
+ return '';
93
+ return /[.!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
94
+ }
95
+ function toolParameter(param) {
96
+ const said = param.description?.trim();
97
+ const guidance = scalarFormatGuidance(param.type);
98
+ const out = {
99
+ name: param.name,
100
+ type: jsonType(param.type),
101
+ description: said ?
102
+ (guidance ? `${sentence(said)} ${guidance}` : sentence(said)) :
103
+ `The ${param.name}, as ${article(param.type)}.`,
104
+ required: isParameterRequired(param),
105
+ };
106
+ if (param.default !== undefined)
107
+ out.default = param.default;
108
+ return out;
109
+ }
110
+ function scalarFormatGuidance(dataType) {
111
+ switch (dataType) {
112
+ case 'Date':
113
+ case 'Time':
114
+ case 'DateTime':
115
+ case 'DateTimeTz':
116
+ return `As ${article(dataType)}.`;
117
+ default:
118
+ return undefined;
119
+ }
120
+ }
121
+ function jsonType(dataType) {
122
+ switch (dataType) {
123
+ case 'Integer':
124
+ return 'integer';
125
+ case 'Decimal':
126
+ case 'Float':
127
+ return 'number';
128
+ case 'Boolean':
129
+ return 'boolean';
130
+ default:
131
+ return 'string';
132
+ }
133
+ }
134
+ function article(dataType) {
135
+ switch (dataType) {
136
+ case 'Integer':
137
+ return 'a whole number';
138
+ case 'Decimal':
139
+ return 'a decimal number';
140
+ case 'Float':
141
+ return 'a number';
142
+ case 'Boolean':
143
+ return 'true or false';
144
+ case 'Date':
145
+ return 'a date, YYYY-MM-DD';
146
+ case 'Time':
147
+ return 'a time, HH:MM:SS';
148
+ case 'DateTime':
149
+ case 'DateTimeTz':
150
+ return 'a timestamp in RFC 3339 form';
151
+ default:
152
+ return 'text';
153
+ }
154
+ }
155
+ function snakeCase(name) {
156
+ return name.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
157
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
158
+ .replace(/[\s-]+/g, '_')
159
+ .toLowerCase();
160
+ }
161
+ /**
162
+ * Every tool a model offers, with the names guaranteed distinct.
163
+ */
164
+ function modelTools(opts) {
165
+ const actions = actionTools(opts);
166
+ const taken = new Set();
167
+ for (const tool of actions) {
168
+ tool.name = distinct(tool.name, taken);
169
+ }
170
+ return {
171
+ actions,
172
+ instruction: instructionFor(opts.runtime.model),
173
+ };
174
+ }
175
+ function instructionFor(model) {
176
+ const parts = [];
177
+ const stated = model.aiContext?.instructions?.trim();
178
+ if (stated)
179
+ parts.push(stated);
180
+ parts.push('Never invent an identifier. When you are given a name or a ' +
181
+ 'description where an action wants a key, ask the caller or read the ' +
182
+ 'store directly. Check every rule that gates an action before running ' +
183
+ 'it: when a rule says a write must not happen, refuse and explain why; ' +
184
+ 'when it says a person has to decide, say so and stop, because you ' +
185
+ 'cannot approve it yourself; when an advisory rule goes unmet, report ' +
186
+ 'both the change and the warning. Finish by saying what you changed.');
187
+ return parts.join('\n\n');
188
+ }
189
+ function distinct(base, taken) {
190
+ let name = base;
191
+ for (let n = 2; taken.has(name); n++)
192
+ name = `${base}_${n}`;
193
+ taken.add(name);
194
+ return name;
195
+ }
196
+ // The fields of `entity` bound to one column each. A binding is read without
197
+ // its comments, as the graph generators read it, so `order_id -- the key`
198
+ // names the column `order_id` on the entity and on every entity that inherits
199
+ // it. On a PostgreSQL store the binding is the text the database runs, the
200
+ // field's ALLOYDB or ANSI_SQL entry, read as PostgreSQL, where `#` starts no
201
+ // comment. A field with neither keeps its binding as fieldBinding gives it,
202
+ // read the same way.
203
+ function boundFields(entity, dialect) {
204
+ const postgres = dialect.name === 'PostgreSQL';
205
+ const bound = [];
206
+ for (const field of entity.fields) {
207
+ const runs = postgres ? (0, ir_1.expressionForDialect)(field, 'ALLOYDB') : undefined;
208
+ const read = runs !== undefined ?
209
+ (0, sql_expr_utils_1.sqlWithoutComments)(runs, 'POSTGRES') :
210
+ (0, sql_expr_utils_1.fieldBindingWithoutComments)(field, postgres ? 'POSTGRES' : undefined);
211
+ const expr = read?.trim() ?? '';
212
+ if (!expr || !/^[A-Za-z_]\w*$/.test(expr))
213
+ continue;
214
+ const said = field.description?.trim();
215
+ bound.push({
216
+ name: field.name,
217
+ type: field.type ?? 'String',
218
+ column: expr,
219
+ ...(said ? { description: said } : {}),
220
+ });
221
+ }
222
+ return bound;
223
+ }
224
+ /**
225
+ * What there is to read under this runtime: one entry per entity the model
226
+ * declares, the profile binds to a table, and a statement can name.
227
+ */
228
+ function readableEntities(runtime, dialect) {
229
+ const readable = [];
230
+ for (const entity of runtime.model.entities ?? []) {
231
+ if (entity.abstract)
232
+ continue;
233
+ const fields = boundFields(entity, dialect);
234
+ if (!fields.length)
235
+ continue;
236
+ const warnings = [];
237
+ const table = (0, binding_1.boundTable)(entity.dataSource, warnings, entity.name, dialect.quote);
238
+ if (warnings.length)
239
+ continue;
240
+ readable.push({ entity, table, fields });
241
+ }
242
+ return readable;
243
+ }
@@ -0,0 +1,13 @@
1
+ import { Store } from './store';
2
+ export interface SqlDialect {
3
+ /** Which dialect this is, for a message that needs to name it. */
4
+ readonly name: 'GoogleSQL' | 'PostgreSQL';
5
+ /** An identifier, quoted as this dialect requires. */
6
+ quote(identifier: string): string;
7
+ }
8
+ /** Spanner and BigQuery. */
9
+ export declare const GOOGLE_SQL: SqlDialect;
10
+ /** AlloyDB (PostgreSQL). */
11
+ export declare const POSTGRESQL: SqlDialect;
12
+ /** The dialect `store` speaks; defaults to GoogleSQL when no store is bound. */
13
+ export declare function dialectFor(store: Store | undefined): SqlDialect;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ // The SQL dialect a bound store speaks (`GoogleSQL` for Spanner and BigQuery,
3
+ // `PostgreSQL` for AlloyDB).
4
+ //
5
+ // `kcmd skills-generate` uses this surface when emitting the physical table
6
+ // and column map in `SKILL.md` (`readableEntities` in `agent_tools.ts` and
7
+ // `readableSchema` in `skills.ts`):
8
+ // - `name`: names the dialect the store expects (`GoogleSQL` or `PostgreSQL`).
9
+ // - `quote`: quotes table and column identifiers according to that dialect's
10
+ // rules (backticks only when needed in GoogleSQL; double quotes in
11
+ // PostgreSQL so mixed-case physical names survive folding).
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.POSTGRESQL = exports.GOOGLE_SQL = void 0;
14
+ exports.dialectFor = dialectFor;
15
+ const sql_identifiers_1 = require("../sql_identifiers");
16
+ /** Spanner and BigQuery. */
17
+ exports.GOOGLE_SQL = {
18
+ name: 'GoogleSQL',
19
+ // Quote only what would otherwise be misread -- a reserved word, or a name
20
+ // that is not a bare identifier. GoogleSQL is case-preserving and
21
+ // case-insensitive, so an ordinary name reaches the column it names without
22
+ // help, and backticking every one of them would churn the table map for no
23
+ // gain.
24
+ quote: sql_identifiers_1.quoteIdentifier,
25
+ };
26
+ /** AlloyDB (PostgreSQL). */
27
+ exports.POSTGRESQL = {
28
+ name: 'PostgreSQL',
29
+ // PostgreSQL folds an unquoted identifier to lowercase before looking it up,
30
+ // so `OrderId` unquoted asks for a column named `orderid` and misses one
31
+ // created as `"OrderId"`. Quoting unconditionally is the one rule that
32
+ // reaches the column however the schema was written, and escapes an embedded
33
+ // quote by doubling it.
34
+ quote: id => `"${id.replace(/"/g, '""')}"`,
35
+ };
36
+ /** The dialect `store` speaks; defaults to GoogleSQL when no store is bound. */
37
+ function dialectFor(store) {
38
+ return store?.kind === 'alloydb' ? exports.POSTGRESQL : exports.GOOGLE_SQL;
39
+ }
@@ -0,0 +1,94 @@
1
+ import * as context from '../../gcp/context';
2
+ import { CatalogSnapshot } from '../../snapshot';
3
+ import { SemanticModel } from '../ir';
4
+ import { Store } from './store';
5
+ /**
6
+ * A semantic model made operational: the model as authored under one binding
7
+ * profile, and the store it runs against.
8
+ */
9
+ export interface SemanticRuntime {
10
+ model: SemanticModel;
11
+ /**
12
+ * The model file this was authored in -- the `.yaml` basename, not a path.
13
+ * Carried so a message about this model can point at the author's file.
14
+ */
15
+ document: string;
16
+ /** Where the model's data lives. Absent when this profile binds no store. */
17
+ store?: Store;
18
+ /** Why there is no store. Set exactly when `store` is absent. */
19
+ storeError?: string;
20
+ /**
21
+ * Set when the model's deployments name a target and still no store
22
+ * results. The target of any deployment may be in no accepted form, the
23
+ * profile's several targets may leave the store ambiguous, the model may
24
+ * bind a table outside its Spanner or AlloyDB store, or finding how to
25
+ * connect may fail, such as the lookup of an AlloyDB cluster's primary
26
+ * instance. Also set when a GOOGLE block's data is not valid JSON, so its
27
+ * targets cannot be read. `storeError` says why.
28
+ */
29
+ storeFailed?: boolean;
30
+ /**
31
+ * Set with `storeFailed` when the model's deployments give no store
32
+ * whatever the model is used for. One such case is a target in
33
+ * no accepted form in any deployment. Another is a GOOGLE block that is not
34
+ * valid JSON. A third is several targets for the profile with no single
35
+ * store. A table bound outside the store and a failed AlloyDB lookup are
36
+ * not such cases.
37
+ */
38
+ deploymentsRejected?: boolean;
39
+ /** Which binding profile produced this. Provenance, for messages. */
40
+ profile: string;
41
+ /** The entry group the model is scoped to. */
42
+ entryGroup: string;
43
+ }
44
+ /**
45
+ * Why this runtime cannot execute SQL DML statements, or null when it can.
46
+ *
47
+ * Having a store is the whole requirement. Every backend a profile can deploy
48
+ * to -- Spanner, AlloyDB, BigQuery -- executes DML, so the kind of store is not
49
+ * a reason to refuse an action.
50
+ */
51
+ export declare function runtimeStoreError(runtime: SemanticRuntime): string | null;
52
+ /** How to create the runtimes. Every field has the answer `kcmd` would give. */
53
+ export interface CreateRuntimeOptions {
54
+ /** Directory holding `catalog.yaml`. Defaults to the current directory. */
55
+ path?: string;
56
+ /**
57
+ * Binding profile to apply to each model. Defaults to the model's inline
58
+ * bindings.
59
+ */
60
+ profile?: string;
61
+ ctx?: context.ApiContext;
62
+ /**
63
+ * Where a non-fatal problem goes -- an unbound field, a profile that names
64
+ * something the model does not. Defaults to `console.warn`. A caller with a
65
+ * transcript to keep tidy can collect them instead.
66
+ */
67
+ onWarning?: (text: string) => void;
68
+ /**
69
+ * Set to refuse a scope that holds more than one model. The scope is
70
+ * refused before any store is looked up. When this is a function, it takes
71
+ * the number of models and returns the error.
72
+ */
73
+ oneModel?: boolean | ((count: number) => string);
74
+ /** The scope, already loaded. Defaults to a load of `path`. */
75
+ snapshot?: CatalogSnapshot;
76
+ }
77
+ /**
78
+ * One runtime per model document in the scope, in discovery order.
79
+ *
80
+ * A lighter path than push's: nothing is pruned or validated for
81
+ * deployment, because running an action needs the model as authored rather
82
+ * than the subset a deployed graph can answer. Bindings stay optional, so a
83
+ * purely logical model yields a runtime with no store -- readable and
84
+ * inspectable, not runnable -- instead of an error that stops the scope.
85
+ */
86
+ export declare function createSemanticRuntimes(options?: CreateRuntimeOptions): Promise<SemanticRuntime[] | {
87
+ error: string;
88
+ }>;
89
+ /**
90
+ * Why `model` cannot be used because a deployment names a binding profile
91
+ * `declared` does not hold, or undefined when each names `default` or a
92
+ * declared profile. `document` is the model's document, for the message.
93
+ */
94
+ export declare function undeclaredProfileError(document: string, model: SemanticModel, declared: string[]): string | undefined;