@cspeach/cli 0.9.0 → 1.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.
- package/README.md +1 -1
- package/dist/agent/intent-system-prompt.js +1 -1
- package/dist/agent/loop.js +228 -26
- package/dist/agent/providers/license-gate.js +44 -0
- package/dist/agent/skill-checkpoint.js +1 -1
- package/dist/agent/tool-dispatch.js +15 -0
- package/dist/approvals/canonical.js +91 -0
- package/dist/approvals/jwt.js +39 -2
- package/dist/approvals/op-labels.js +124 -0
- package/dist/approvals/render.js +42 -36
- package/dist/auth/org-anthropic-key.js +25 -0
- package/dist/classifier/client.js +18 -3
- package/dist/cli.js +15 -0
- package/dist/commands/compact.js +28 -2
- package/dist/commands/config-set.js +284 -0
- package/dist/commands/config-show.js +20 -0
- package/dist/commands/export-audit.js +43 -0
- package/dist/commands/help.js +5 -0
- package/dist/commands/login.js +31 -14
- package/dist/commands/plan-audit-evidence.js +266 -0
- package/dist/commands/plan-audit.js +692 -0
- package/dist/commands/plan-chain.js +671 -0
- package/dist/commands/plan-continue.js +179 -0
- package/dist/commands/plan-gate.js +154 -0
- package/dist/commands/plan-model-tier.js +83 -0
- package/dist/commands/plan-resume.js +728 -46
- package/dist/config/loader.js +223 -5
- package/dist/config/model-defaults.js +14 -0
- package/dist/cost/pricing.js +27 -1
- package/dist/doctor/checks/_http-probe.js +1 -0
- package/dist/doctor/checks/cert.js +14 -3
- package/dist/doctor/checks/sap.js +30 -8
- package/dist/doctor/checks/system-roles.js +41 -0
- package/dist/doctor/checks/zcspeach.js +19 -4
- package/dist/doctor/run.js +2 -0
- package/dist/models/resolve.js +61 -0
- package/dist/models/server-config.js +155 -0
- package/dist/one-shot.js +76 -6
- package/dist/projects/answer-blockers.js +137 -0
- package/dist/projects/extract-cca.js +111 -17
- package/dist/projects/extract-modernize.js +4 -2
- package/dist/projects/extract-plan.js +184 -37
- package/dist/projects/extract-spec-gap.js +34 -7
- package/dist/projects/extract-test-coverage.js +4 -2
- package/dist/projects/extract-upgrade.js +116 -23
- package/dist/projects/handover-md.js +195 -0
- package/dist/projects/index.js +5 -2
- package/dist/projects/merge-cca.js +292 -0
- package/dist/projects/merge-upgrade.js +173 -0
- package/dist/projects/migration.js +103 -1
- package/dist/projects/output-paths.js +27 -0
- package/dist/projects/plan-run.js +285 -27
- package/dist/projects/plan-schema.js +136 -3
- package/dist/projects/promote-command.js +25 -2
- package/dist/projects/promote.js +128 -0
- package/dist/projects/run-lease.js +157 -0
- package/dist/projects/save-command.js +259 -21
- package/dist/projects/status.js +3 -1
- package/dist/projects/validate.js +1 -1
- package/dist/projects/workspace.js +164 -20
- package/dist/renderer/notices.js +64 -0
- package/dist/renderer/progress-chatter.js +8 -0
- package/dist/renderer/status-footer.js +22 -12
- package/dist/renderer/thinking-heartbeat.js +64 -8
- package/dist/renderer/todo-block.js +51 -0
- package/dist/renderer/tool-widget.js +55 -4
- package/dist/renderer/tty.js +43 -4
- package/dist/renderer/verify-chain.js +77 -0
- package/dist/repl/at-picker.js +60 -7
- package/dist/repl/bracketed-paste.js +28 -19
- package/dist/repl/builtin-commands.js +42 -0
- package/dist/repl/current-transport.js +10 -0
- package/dist/repl/early-line-buffer.js +68 -0
- package/dist/repl/history.js +86 -0
- package/dist/repl/ink-stdin-guard.js +64 -0
- package/dist/repl/inquirer-guard.js +70 -5
- package/dist/repl/mode-ceiling.js +16 -0
- package/dist/repl/mode-cycle.js +104 -0
- package/dist/repl/numbered-menu.js +131 -0
- package/dist/repl/post-turn-status.js +26 -6
- package/dist/repl/rule8-detector.js +17 -2
- package/dist/repl/safety-confirm.js +111 -2
- package/dist/repl/safety-mode-state.js +19 -3
- package/dist/repl/slash-completer.js +5 -0
- package/dist/repl/slash-picker.js +10 -15
- package/dist/repl.js +1232 -95
- package/dist/rewind/candidates.js +194 -0
- package/dist/rewind/cli.js +137 -0
- package/dist/rewind/format.js +27 -0
- package/dist/rewind/restore.js +245 -0
- package/dist/router/classifier.js +150 -6
- package/dist/sap/capability-matrix.js +20 -0
- package/dist/sap/capability-matrix.json +11236 -0
- package/dist/sap/capability.js +146 -0
- package/dist/sap/connection-manager.js +19 -1
- package/dist/sap/onboarding.js +42 -4
- package/dist/session/audit-export.js +459 -0
- package/dist/session/context-report.js +163 -0
- package/dist/session/pending.js +27 -0
- package/dist/session/recap.js +160 -0
- package/dist/skill-catalog.js +51 -40
- package/dist/skills/bundled-skills.js +272 -1
- package/dist/skills/promotion-dispatch.js +23 -0
- package/dist/tools/_command-shared.js +36 -12
- package/dist/tools/_filesystem-shared.js +139 -4
- package/dist/tools/_flag.js +25 -0
- package/dist/tools/approval.js +177 -26
- package/dist/tools/ask-question.js +400 -7
- package/dist/tools/capability/tool.js +74 -0
- package/dist/tools/dispatch-skill.js +22 -1
- package/dist/tools/extend-model/anchored-insert.js +1414 -0
- package/dist/tools/extend-model/tool.js +340 -0
- package/dist/tools/filesystem/extract-document.js +57 -0
- package/dist/tools/filesystem/file-edit.js +12 -2
- package/dist/tools/filesystem/file-read.js +2 -2
- package/dist/tools/filesystem/file-write.js +11 -2
- package/dist/tools/filesystem/glob.js +11 -0
- package/dist/tools/filesystem/grep.js +10 -0
- package/dist/tools/filesystem/read-document.js +107 -0
- package/dist/tools/fiori/apply.js +50 -0
- package/dist/tools/fiori/bin.js +3 -0
- package/dist/tools/fiori/catalog/index.js +27 -0
- package/dist/tools/fiori/catalog/value-help.js +230 -0
- package/dist/tools/fiori/catalog/viz-chart.js +177 -0
- package/dist/tools/fiori/cli.js +71 -0
- package/dist/tools/fiori/deploy-config.js +73 -0
- package/dist/tools/fiori/fe-extend.js +76 -0
- package/dist/tools/fiori/fe-scaffold.js +71 -0
- package/dist/tools/fiori/floorplan-map.js +19 -0
- package/dist/tools/fiori/i18n.js +39 -0
- package/dist/tools/fiori/manifest.js +70 -0
- package/dist/tools/fiori/render.js +77 -0
- package/dist/tools/fiori/samples/data/index.json +13602 -0
- package/dist/tools/fiori/samples/data/sources.generated.js +808 -0
- package/dist/tools/fiori/samples/loader.js +248 -0
- package/dist/tools/fiori/samples/search.js +63 -0
- package/dist/tools/fiori/samples/types.js +2 -0
- package/dist/tools/fiori/scaffold.js +39 -0
- package/dist/tools/fiori/smoke/assertions.js +74 -0
- package/dist/tools/fiori/smoke/browser.js +52 -0
- package/dist/tools/fiori/smoke/driver.js +89 -0
- package/dist/tools/fiori/smoke/freestyle-spec.js +317 -0
- package/dist/tools/fiori/smoke/run-smoke.js +149 -0
- package/dist/tools/fiori/tools.js +681 -0
- package/dist/tools/fiori/types.js +1 -0
- package/dist/tools/local-build.js +86 -0
- package/dist/tools/local-files.js +31 -0
- package/dist/tools/project/_merge-shared.js +68 -0
- package/dist/tools/project/cca_merge.js +164 -0
- package/dist/tools/project/playbook_get.js +1 -1
- package/dist/tools/project/upgrade_merge_progress.js +206 -0
- package/dist/tools/sap-read.js +132 -20
- package/dist/tools/sap-write.js +550 -21
- package/dist/tools/shell/shell_exec.js +41 -6
- package/dist/tools/snapshot.js +63 -14
- package/dist/tools/subagent/agent_run.js +27 -3
- package/dist/tools/subagent/background_run.js +17 -1
- package/dist/tools/todo.js +144 -0
- package/dist/tools/transport-resolution.js +86 -0
- package/dist/tools/transport.js +224 -5
- package/dist/tools/write-mode.js +4 -0
- package/dist/ui/app.js +378 -21
- package/dist/ui/approval-modal.js +49 -16
- package/dist/ui/ask-question-emitter.js +14 -0
- package/dist/ui/body.js +13 -0
- package/dist/ui/context-grid.js +108 -0
- package/dist/ui/footer.js +120 -27
- package/dist/ui/header.js +7 -0
- package/dist/ui/line-resolution.js +35 -8
- package/dist/ui/rewind-emitter.js +10 -0
- package/dist/ui/rewind-panel.js +81 -0
- package/dist/ui/sap-state-store.js +1 -0
- package/dist/ui/session-timeline.js +1 -0
- package/dist/ui/status-line.js +43 -0
- package/dist/ui/text-input.js +214 -0
- package/dist/ui/todo-emitter.js +25 -0
- package/dist/ui/todo-panel.js +64 -0
- package/dist/ui/turn-status-emitter.js +50 -4
- package/dist/ui/turn-status.js +18 -3
- package/dist/ui/widgets/ask-form.js +242 -0
- package/dist/ui/widgets/ask-question-modal.js +21 -8
- package/package.json +22 -3
- package/bench/README.md +0 -78
- package/bench/prompts/abap-document-cds.md +0 -44
- package/bench/prompts/abap-explain-bdef-handler.md +0 -57
- package/bench/prompts/abap-test-method.md +0 -42
- package/bench/results/abap-document-cds/claude-haiku-4-5.md +0 -189
- package/bench/results/abap-document-cds/claude-opus-4-7.md +0 -120
- package/bench/results/abap-document-cds/claude-sonnet-4-6.md +0 -151
- package/bench/results/abap-explain-bdef-handler/claude-haiku-4-5.md +0 -112
- package/bench/results/abap-explain-bdef-handler/claude-opus-4-7.md +0 -101
- package/bench/results/abap-explain-bdef-handler/claude-sonnet-4-6.md +0 -101
- package/bench/results/abap-test-method/claude-haiku-4-5.md +0 -186
- package/bench/results/abap-test-method/claude-opus-4-7.md +0 -193
- package/bench/results/abap-test-method/claude-sonnet-4-6.md +0 -234
package/dist/tools/sap-read.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* searchObject(query, objType?, maxResults?)
|
|
8
8
|
* changeLog(type, name)
|
|
9
9
|
* sqlQuery(query, maxRows?, extraBlocked?)
|
|
10
|
-
* shortDump(dumpId?, user?)
|
|
10
|
+
* shortDump(dumpId?, user?, fromDate?, toDate?)
|
|
11
11
|
* atcRun(type, name, variant?)
|
|
12
12
|
* usageReferences(objectType, objectName)
|
|
13
13
|
* apiState(objects)
|
|
@@ -32,7 +32,9 @@ registerTool({
|
|
|
32
32
|
+ 'omitting it lets SAP choose (usually active). When the object has '
|
|
33
33
|
+ 'pending inactive changes and the caller did not pass `version`, '
|
|
34
34
|
+ 'the response is the active source AND a warning is emitted so the '
|
|
35
|
-
+ 'caller knows the inactive edits are not visible.'
|
|
35
|
+
+ 'caller knows the inactive edits are not visible. For a freshly '
|
|
36
|
+
+ 'created object with NO active version yet, the inactive (working) '
|
|
37
|
+
+ 'source is returned with version="inactive" and an honest note.',
|
|
36
38
|
isMutating: false,
|
|
37
39
|
input_schema: {
|
|
38
40
|
type: 'object',
|
|
@@ -68,15 +70,41 @@ registerTool({
|
|
|
68
70
|
const conflict = inactive.find((o) => o.name.toUpperCase() === upperName &&
|
|
69
71
|
(o.type === '' || o.type.toUpperCase().startsWith(upperType)));
|
|
70
72
|
if (conflict) {
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
// Fable review 2026-07-06 (clas-create-hygiene I1): a FRESH shell
|
|
74
|
+
// (created, never activated) ALSO appears in inactiveObjects(), but
|
|
75
|
+
// it has NO active version at all — sap-client's inactive-read
|
|
76
|
+
// fallback just served the INACTIVE (working) source. Claiming
|
|
77
|
+
// "this is the ACTIVE source" here would be false. Cross-check
|
|
78
|
+
// objectStructure's adtcore:version (honest for fresh shells now
|
|
79
|
+
// that objectStructure carries the same inactive fallback):
|
|
80
|
+
// 'inactive' ⇒ inactive-only shell.
|
|
81
|
+
let inactiveOnly = false;
|
|
82
|
+
try {
|
|
83
|
+
const struct = await ctx.adt.objectStructure(args.type, args.name);
|
|
84
|
+
const m = /\badtcore:version="([^"]*)"/.exec(struct.rawXml ?? '');
|
|
85
|
+
inactiveOnly = m?.[1] === 'inactive';
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Cross-check failed — fall through to the conservative
|
|
89
|
+
// split-state warning (never claim inactive-only without proof).
|
|
90
|
+
}
|
|
91
|
+
if (inactiveOnly) {
|
|
92
|
+
resolvedVersion = 'inactive';
|
|
93
|
+
splitStateWarning =
|
|
94
|
+
`${args.type} ${args.name} has no active version yet — this is the `
|
|
95
|
+
+ `inactive (working) source. Activate it via sap_activate when ready.`;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// SAP defaults to the active version when version is unspecified,
|
|
99
|
+
// but the caller should know an inactive copy exists so they don't
|
|
100
|
+
// base dependent edits on stale committed source.
|
|
101
|
+
resolvedVersion = 'active';
|
|
102
|
+
splitStateWarning =
|
|
103
|
+
`${args.type} ${args.name} has a pending inactive version. `
|
|
104
|
+
+ `This response is the ACTIVE source; pending edits are NOT included. `
|
|
105
|
+
+ `Pass version="inactive" to read the in-flight version, or have the user `
|
|
106
|
+
+ `activate (or discard) the inactive version before basing dependent code on this read.`;
|
|
107
|
+
}
|
|
80
108
|
}
|
|
81
109
|
else {
|
|
82
110
|
resolvedVersion = 'active';
|
|
@@ -112,7 +140,22 @@ registerTool({
|
|
|
112
140
|
},
|
|
113
141
|
handler: async (args, ctx) => {
|
|
114
142
|
const result = await ctx.adt.objectStructure(args.type, args.name);
|
|
115
|
-
|
|
143
|
+
// Fable review 2026-07-05 (audit-reverify item 2) — surface the object's
|
|
144
|
+
// adtcore:version EARLY in the JSON. The sap-client ObjectStructure has
|
|
145
|
+
// no parsed version field; adtcore:version lives deep inside rawXml,
|
|
146
|
+
// beyond the audit evidence's 200-char result excerpt — so the audit
|
|
147
|
+
// contract's "sap_object_structure reporting an active version" proof
|
|
148
|
+
// path for verify-only re-runs was unreachable in production, and a
|
|
149
|
+
// re-run that skipped sap_inactive_objects would false-FAIL an active
|
|
150
|
+
// object. Parsing here (CLI-side, no sap-client bump) and placing
|
|
151
|
+
// `version` right after name/type puts it inside the excerpt window.
|
|
152
|
+
const versionMatch = /\badtcore:version="([^"]*)"/.exec(result.rawXml ?? '');
|
|
153
|
+
const version = versionMatch?.[1] || 'unknown';
|
|
154
|
+
// name/type/version lead the serialized JSON (rawXml stays last via the
|
|
155
|
+
// rest spread), so `version` sits inside the excerpt window while every
|
|
156
|
+
// ObjectStructure field is preserved.
|
|
157
|
+
const { name, type, ...rest } = result;
|
|
158
|
+
return { content: JSON.stringify({ name, type, version, ...rest }, null, 2) };
|
|
116
159
|
},
|
|
117
160
|
});
|
|
118
161
|
// -----------------------------------------------------------------------
|
|
@@ -167,7 +210,9 @@ registerTool({
|
|
|
167
210
|
// -----------------------------------------------------------------------
|
|
168
211
|
registerTool({
|
|
169
212
|
name: 'sap_sql_query',
|
|
170
|
-
description: 'Execute a read-only SQL SELECT against the SAP database via ADT Data Preview. Security-sensitive tables are blocked automatically.'
|
|
213
|
+
description: 'Execute a read-only SQL SELECT against the SAP database via ADT Data Preview. Security-sensitive tables are blocked automatically. '
|
|
214
|
+
+ 'Dialect limits (violations return an opaque "SAP HTTP 400: sqlQuery"): no IN-lists and at most one LIKE per WHERE clause '
|
|
215
|
+
+ '(multi-OR LIKE chains are rejected) — use a single LIKE or split into separate queries.',
|
|
171
216
|
isMutating: false,
|
|
172
217
|
input_schema: {
|
|
173
218
|
type: 'object',
|
|
@@ -187,7 +232,10 @@ registerTool({
|
|
|
187
232
|
// -----------------------------------------------------------------------
|
|
188
233
|
registerTool({
|
|
189
234
|
name: 'sap_short_dump',
|
|
190
|
-
description: 'Read ABAP runtime errors (short dumps / ST22). Without dumpId returns
|
|
235
|
+
description: 'Read ABAP runtime errors (short dumps / ST22). Without dumpId returns dumps from a '
|
|
236
|
+
+ 'time window (default: the last 24h of SAP SYSTEM time), paginated server-side; the '
|
|
237
|
+
+ 'result includes a `coverage` line stating exactly which window was fetched — trust it '
|
|
238
|
+
+ 'over assumptions. With dumpId returns the full diagnostic detail.',
|
|
191
239
|
isMutating: false,
|
|
192
240
|
input_schema: {
|
|
193
241
|
type: 'object',
|
|
@@ -198,13 +246,43 @@ registerTool({
|
|
|
198
246
|
},
|
|
199
247
|
user: {
|
|
200
248
|
type: 'string',
|
|
201
|
-
description: 'Filter dumps by SAP username (list mode only)',
|
|
249
|
+
description: 'Filter dumps by SAP username (list mode only, applied after the windowed fetch)',
|
|
250
|
+
},
|
|
251
|
+
fromDate: {
|
|
252
|
+
type: 'string',
|
|
253
|
+
description: 'Window start (list mode). Zone-less values are SAP SYSTEM time: "YYYYMMDDHHMMSS", '
|
|
254
|
+
+ '"YYYY-MM-DD" (day start) or "2026-06-11T06:00:00". ISO-8601 with timezone '
|
|
255
|
+
+ '("2026-06-11T06:00:00Z") is an absolute instant, auto-translated to system time. '
|
|
256
|
+
+ 'Default: now − 24h (or 24h before an explicit toDate).',
|
|
257
|
+
},
|
|
258
|
+
toDate: {
|
|
259
|
+
type: 'string',
|
|
260
|
+
description: 'Window end (list mode). Same formats as fromDate ("YYYY-MM-DD" = day end). '
|
|
261
|
+
+ 'Default: now (SAP system time).',
|
|
202
262
|
},
|
|
203
263
|
},
|
|
204
264
|
required: [],
|
|
205
265
|
},
|
|
206
266
|
handler: async (args, ctx) => {
|
|
207
|
-
const result = await ctx.adt.shortDump(args.dumpId, args.user);
|
|
267
|
+
const result = await ctx.adt.shortDump(args.dumpId, args.user, args.fromDate, args.toDate);
|
|
268
|
+
// List mode: lead with a coverage line so the model KNOWS what slice of
|
|
269
|
+
// time it is looking at (battery D18 — the old list was the feed's
|
|
270
|
+
// arbitrary first page and the newest dumps were routinely missing).
|
|
271
|
+
if (result.operation === 'list' && result.window) {
|
|
272
|
+
const w = result.window;
|
|
273
|
+
let coverage = `showing ${result.count} dump(s) from window ${w.windowFromSap}–${w.windowToSap} `
|
|
274
|
+
+ `(SAP system time${w.windowFromUtc ? `; UTC ${w.windowFromUtc}–${w.windowToUtc}` : ''}); `
|
|
275
|
+
+ `${w.totalFetched} in window before filters; ${w.pagesFetched} page(s) fetched`;
|
|
276
|
+
if (w.clockProbe === 'failed') {
|
|
277
|
+
coverage +=
|
|
278
|
+
'; WARNING: system clock probe failed — window widened by ±1 day, timestamps are approximate';
|
|
279
|
+
}
|
|
280
|
+
if (w.capped) {
|
|
281
|
+
coverage +=
|
|
282
|
+
'; WARNING: page cap hit — window NOT fully covered, narrow fromDate/toDate to see the rest';
|
|
283
|
+
}
|
|
284
|
+
return { content: JSON.stringify({ coverage, ...result }, null, 2) };
|
|
285
|
+
}
|
|
208
286
|
return { content: JSON.stringify(result, null, 2) };
|
|
209
287
|
},
|
|
210
288
|
});
|
|
@@ -440,6 +518,31 @@ registerTool({
|
|
|
440
518
|
handler: async (args, ctx) => {
|
|
441
519
|
// runUnitTest(type, name) — type first
|
|
442
520
|
const result = await ctx.adt.runUnitTest(args.type, args.name);
|
|
521
|
+
// B2 false-green guard (project_aunit_run_gap): the ADT AUnit-run endpoint
|
|
522
|
+
// returns HTTP 200 with an EMPTY runResult (total:0, no programs) even for a
|
|
523
|
+
// class that is active + syntax-clean with a valid FOR TESTING local class —
|
|
524
|
+
// a known environmental gap, exhaustively probed (sap-client buildUnitTestXml
|
|
525
|
+
// comment). The raw {total:0} shape reads to a caller like "0 tests, nothing
|
|
526
|
+
// failed → green", so an unguarded pass-through risks a false-green test gate.
|
|
527
|
+
// When we detect that empty signature, wrap the (still-carried) raw result in
|
|
528
|
+
// an explicit UNVERIFIED envelope that names Eclipse "Run As → ABAP Unit" as
|
|
529
|
+
// the authoritative check. A genuine run (total > 0) passes through unchanged.
|
|
530
|
+
const summary = result?.summary;
|
|
531
|
+
const programs = result?.programs;
|
|
532
|
+
const noTestsRun = summary?.total === 0 && (!Array.isArray(programs) || programs.length === 0);
|
|
533
|
+
if (noTestsRun) {
|
|
534
|
+
return {
|
|
535
|
+
content: JSON.stringify({
|
|
536
|
+
verification: 'UNVERIFIED',
|
|
537
|
+
warning: 'ABAP Unit run enumerated 0 tests (empty runResult). This does NOT mean the tests ' +
|
|
538
|
+
'passed — it means none executed. A FOR TESTING class that is active + syntax-clean ' +
|
|
539
|
+
'still returning 0 is a known ADT AUnit-run gap (project_aunit_run_gap): the ' +
|
|
540
|
+
'authoritative check is Eclipse "Run As → ABAP Unit" (Ctrl+Shift+F10). Do NOT treat ' +
|
|
541
|
+
'this as a green test gate.',
|
|
542
|
+
result,
|
|
543
|
+
}, null, 2),
|
|
544
|
+
};
|
|
545
|
+
}
|
|
443
546
|
return { content: JSON.stringify(result, null, 2) };
|
|
444
547
|
},
|
|
445
548
|
});
|
|
@@ -448,18 +551,27 @@ registerTool({
|
|
|
448
551
|
// -----------------------------------------------------------------------
|
|
449
552
|
registerTool({
|
|
450
553
|
name: 'sap_odata_test',
|
|
451
|
-
description: 'Test an OData service endpoint
|
|
554
|
+
description: 'Test an OData service endpoint (V2 or V4). Fetches $metadata or reads an entity set and returns the real HTTP status, the request URL/Accept actually used, and the response body. Works on draft-enabled OData V4 RAP services: V4 is auto-detected from /odata4/ paths, $metadata gets an XML Accept, and data reads use a JSON Accept without the V2-only $format option. System query options ($metadata, $top, $filter, $select, $expand, $count) are preserved literally.',
|
|
452
555
|
isMutating: false,
|
|
453
556
|
input_schema: {
|
|
454
557
|
type: 'object',
|
|
455
558
|
properties: {
|
|
456
559
|
servicePath: {
|
|
457
560
|
type: 'string',
|
|
458
|
-
description: 'Full service path
|
|
561
|
+
description: 'Full service path. V2 e.g. /sap/opu/odata/sap/ZSALESORDER_SD_SRV; V4 e.g. /sap/opu/odata4/sap/zui_x_o4/srvd/sap/zui_x/0001',
|
|
459
562
|
},
|
|
460
563
|
entity: {
|
|
461
564
|
type: 'string',
|
|
462
|
-
description: 'Optional entity set name to append, e.g. SalesOrder',
|
|
565
|
+
description: 'Optional entity set name to append and read, e.g. SalesOrder',
|
|
566
|
+
},
|
|
567
|
+
query: {
|
|
568
|
+
type: 'string',
|
|
569
|
+
description: 'Optional raw OData query/resource. Pass "$metadata" to fetch the metadata document, or query options like "$top=5&$filter=Status eq \'OPEN\'". $ signs are preserved as-is. Overrides the default $top=5 when an entity is given.',
|
|
570
|
+
},
|
|
571
|
+
version: {
|
|
572
|
+
type: 'string',
|
|
573
|
+
enum: ['v2', 'v4'],
|
|
574
|
+
description: 'OData version override. Normally auto-detected (/odata4/ ⇒ v4, else v2). Set this only when the path does not reveal the version.',
|
|
463
575
|
},
|
|
464
576
|
method: {
|
|
465
577
|
type: 'string',
|
|
@@ -470,7 +582,7 @@ registerTool({
|
|
|
470
582
|
required: ['servicePath'],
|
|
471
583
|
},
|
|
472
584
|
handler: async (args, ctx) => {
|
|
473
|
-
const result = await ctx.adt.odataTest(args.servicePath, args.entity, args.method ?? 'GET');
|
|
585
|
+
const result = await ctx.adt.odataTest(args.servicePath, args.entity, args.method ?? 'GET', { query: args.query, version: args.version });
|
|
474
586
|
return { content: JSON.stringify(result, null, 2) };
|
|
475
587
|
},
|
|
476
588
|
});
|