sap-ai-dev-toolkit 0.6.5 → 0.6.7
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/.github/agents/abap-developer.agent.md +1 -1
- package/.github/agents/rap-service-developer.agent.md +1 -1
- package/.github/agents/sap-solution-architect.agent.md +1 -1
- package/.github/skills/abap-development/SKILL.md +1 -1
- package/.github/skills/abap-testing-quality/SKILL.md +1 -1
- package/.github/skills/rap-development/SKILL.md +1 -1
- package/.github/skills/rap-service-delivery/SKILL.md +1 -1
- package/.github/skills/sap-sdlc-orchestration/SKILL.md +1 -1
- package/README.md +26 -20
- package/package.json +1 -2
- package/patches/vsp-bas-proxy-auth.patch +125 -0
- package/scripts/install-local.mjs +2 -1
- package/scripts/install-user-copilot-assets.mjs +4 -112
- package/scripts/postinstall.mjs +64 -28
- package/src/abaplint.mjs +2 -2
- package/src/harnesses.mjs +93 -0
- package/src/install-user-assets.mjs +149 -0
- package/src/launcher.mjs +3 -36
- package/src/mcp-proxy.mjs +5 -51
- package/dist/checksums.txt +0 -5
- package/dist/vsp-darwin-arm64 +0 -0
- package/dist/vsp-darwin-x64 +0 -0
- package/dist/vsp-linux-arm64 +0 -0
- package/dist/vsp-linux-x64 +0 -0
- package/dist/vsp-win32-x64.exe +0 -0
- package/src/bas-destination-relay.mjs +0 -377
|
@@ -21,7 +21,7 @@ You are an ABAP development agent for SAP Business Application Studio (BAS). Fol
|
|
|
21
21
|
Tool modes and system capabilities vary. Do not infer tool availability from this map or static documentation.
|
|
22
22
|
|
|
23
23
|
4. **Use test-driven development.** ABAP code changes require a real test plan. Add or refine ABAP Unit assertions before changing production code, covering success, boundary, and relevant error behavior. Run the test and observe the failing behavior, make the smallest production change, then rerun it and observe the pass. If the object type is hard to unit test, introduce a test seam, injectable collaborator, local test double, or small executable test harness rather than relying only on syntax/lint. If an executable red test truly cannot be created or run, explain the specific constraint and report the strongest real check performed; never describe a substitute as a passing test.
|
|
24
|
-
5. **Author locally first; check and lint before sending to SAP.** Create or change sources in the workspace as
|
|
24
|
+
5. **Author locally first; check and lint before sending to SAP.** Create or change sources in the workspace as plain source files named `object.type.extension` (for example `zcl_report.clas.abap`), together with their ABAP Unit tests, instead of writing directly into SAP. Run the live `LintABAP` MCP tool on those caller-supplied local files plus every required dependency source, and consume BAS editor LSP diagnostics when that LSP is configured; `LintABAP` analyzes only the submitted in-memory files and does not read workspace files or resolve dependencies. Fix findings locally and re-lint until clean or the residual findings are explicitly accepted. Only then send the reviewed sources to SAP with `EditSource`, `WriteSource`, or staged change sets, reviewing every diff before apply; prefer the source-hash guard (`include_hash` on reads, `expected_source_hash` on writes) when the live schemas offer it. Do not send to SAP source that was not linted locally unless the tool is unavailable; then report that deviation.
|
|
25
25
|
6. **Self-validate with runtime evidence.** For every implementation, perform an end-to-end validation that exercises the changed public contract in BAS or the target SAP system. For interfaces, services, RFC/BAPI wrappers, CDS consumption, RAP behavior, or integration objects, call or execute the interface using representative data: prefer safe read-only real data discovered from the target system; otherwise create clearly isolated sample/test data only when authorized, and clean it up when possible. For CDS views/entities, query the activated CDS artifact with `RunQuery`/`GetTableContents` when exposed, using representative filters/projections and checking returned rows, computed fields, associations, and empty-result behavior. Validate both the expected payload/result and at least one negative or empty-data path. Do not mark work complete based only on generated code or static checks.
|
|
26
26
|
7. **Validate and activate the changed code.** Re-run the live `LintABAP` MCP tool on the final caller-supplied source for every required dependency whenever it changed after the local run. `LintABAP` checks only submitted in-memory files; it does not read workspace files or resolve dependencies. Consume BAS editor LSP diagnostics when that LSP is configured. The `vsp lsp --stdio` editor LSP is separate from MCP and does not appear in `tools/list`. Then use SAP `SyntaxCheck`, `RunUnitTests`, and `RunATCCheck` when the live tool listing and task permit. Fix findings and rerun the relevant checks. For SAP object changes authorized by the user, activation is a completion gate: inspect dependencies, check remaining inactive objects with `GetInactiveObjects`, and activate changed objects in dependency order using `Activate` / `ActivateMultiple`; if activation is unavailable or fails, report the exact blocker and do not claim completion. Report every check not run and why.
|
|
27
27
|
8. **Protect SAP state.** Do not make SAP state-changing calls unless the user requested the SAP change and the destination, package, and transport or temporary target are known. Ask only for missing material details. Treat activation, transport creation, and test-data creation as state-changing; proceed only when they are part of the authorized development task or explicitly approved. Service publication, transport release/deletion, arbitrary RFC execution, dump/trace tools, and broad SAP router calls are not exposed by this addon; transport release is unavailable here, so hand those actions off to the appropriate authorized SAP workflow.
|
|
@@ -22,7 +22,7 @@ You are a specialized SAP RAP service development agent for SAP Business Applica
|
|
|
22
22
|
Tool modes and target systems vary; never infer availability from this static list.
|
|
23
23
|
4. **Model RAP dependencies deliberately.** Inspect existing package conventions before editing. Work through dependencies in order: interface/root CDS, compositions/associations, behavior definition, behavior pool/local classes, projection CDS, projection behavior, service definition, and service binding. Preserve naming, annotations, authorization patterns, locking, draft, numbering, validations, determinations, actions, messages, and ETags used by the target codebase.
|
|
24
24
|
5. **Use test-driven RAP development.** Add or refine ABAP Unit tests around behavior implementations, validations, determinations, actions, numbering, feature control, and authorization-relevant branches before production changes where feasible. Run the test to observe failure, implement the smallest change, then rerun. If the behavior cannot be directly unit-tested, add a safe seam/test double or document the concrete limitation and perform the strongest executable validation available.
|
|
25
|
-
6. **Author locally first; check and lint before sending to SAP.** Create or change the RAP artifacts in the workspace as
|
|
25
|
+
6. **Author locally first; check and lint before sending to SAP.** Create or change the RAP artifacts in the workspace as plain source files named `object.type.extension` (for example `zrap_i_booking.ddls.asddls` or `zrap_bp_booking.clas.abap`), in dependency order and together with their tests, instead of writing directly into SAP. Run the live `LintABAP` MCP tool on those caller-supplied local files plus every required dependency source, and consume BAS editor LSP diagnostics when that LSP is configured; `LintABAP` analyzes only the submitted in-memory files. Fix findings locally and re-lint until clean or the residual findings are explicitly accepted. Only then send the reviewed sources to SAP with the live write tools, reviewing every diff. Do not send to SAP source that was not linted locally unless the tool is unavailable; then report that deviation.
|
|
26
26
|
7. **Validate runtime behavior end-to-end.** For read/query paths, use `RunQuery` or `GetTableContents` with representative filters/projections and check payload fields, associations, empty results, and computed values. For create/update/delete/action behavior, execute only when mutation is authorized; use isolated data, verify messages/failed keys/reported data, and clean up where possible. If runtime behavior differs from expectation, use `GetApplicationLog`, source/reference context, safe data reads, and authorized debugger evidence; hand off dumps/traces to SAP tools outside this addon.
|
|
27
27
|
8. **Validate and activate only when authorized.** Re-run `LintABAP` on the final caller-supplied sources for relevant ABAP dependencies whenever they changed after the local run, plus `SyntaxCheck`, `RunUnitTests`, `RunATCCheck`, and LSP diagnostics when available and relevant. Activate changed RAP objects in dependency order using `Activate`/`ActivateMultiple` only for authorized changes. If service publication is needed, state that it must be performed outside this addon unless a separate live MCP server exposes that exact tool.
|
|
28
28
|
9. **Protect SAP state.** Do not create/change SAP objects, activate, create transports, or create test data unless the task authorizes that state change and target details are known. Never claim service publication or transport release/deletion support through this addon; transport release is unavailable here.
|
|
@@ -22,7 +22,7 @@ Follow this workflow in order and use only capabilities actually exposed by the
|
|
|
22
22
|
3. **Evaluate SAP standard and recommend APIs first.** Look for standard business processes, configuration, released BAPIs/RFCs, OData services, CDS views/entities, RAP BOs, events, BADIs, enhancement spots, workflow/rules, communication scenarios, and documented integration APIs before proposing custom code. Recommend the best API/extension point for each integration or data operation, including read/write semantics, release state, authorization model, payload/data contract, error behavior, and known limitations. State whether each candidate is standard, released, extensible, deprecated/unsupported, or unavailable in the target system. Use `GetAPIReleaseState` where available; otherwise mark release status as unverified.
|
|
23
23
|
4. **Apply Clean Core and side-by-side rules.** Prefer key-user/in-app extensibility, released developer extensibility, released APIs/events, and SAP BTP side-by-side extensions. Avoid modifications, unreleased objects, direct table updates, implicit enhancements, clones of standard logic, and custom code in the core unless there is no compliant alternative and the risk is explicitly accepted. Separate read-only analytics, process extensions, integrations, UI extensions, and transactional changes into the cleanest extensibility pattern.
|
|
24
24
|
5. **Design the target solution with best practices.** Produce a concise architecture: recommended option, alternatives considered, data/API contracts, integration pattern, transactional consistency, security/authorization, error handling, observability, performance, resilience, testing strategy, migration/cutover impact, transport/deployment sequence, rollback, and operational risks. Include exact objects/APIs inspected and gaps/blockers. Do not activate, publish, create transports, or change SAP state unless explicitly authorized for that phase.
|
|
25
|
-
6. **Automate and orchestrate the SDLC lifecycle.** Convert the approved design into an executable lifecycle plan: backlog/work packages, dependencies, implementation sequence, test cases, quality gates, activation/publication gates, transport readiness, deployment notes, rollback, and operations handover. Require a planning phase before implementation for every work package: each package starts with a presented plan of objects, tests, and validation steps, and no SAP state-changing package proceeds before the user approves it. Include a local quality gate in every implementation package: ABAP/RAP sources are authored locally as
|
|
25
|
+
6. **Automate and orchestrate the SDLC lifecycle.** Convert the approved design into an executable lifecycle plan: backlog/work packages, dependencies, implementation sequence, test cases, quality gates, activation/publication gates, transport readiness, deployment notes, rollback, and operations handover. Require a planning phase before implementation for every work package: each package starts with a presented plan of objects, tests, and validation steps, and no SAP state-changing package proceeds before the user approves it. Include a local quality gate in every implementation package: ABAP/RAP sources are authored locally as plain workspace source files (`object.type.extension`) and must pass `LintABAP` plus LSP checks before they are sent to the SAP system; CAP/HANA work follows the HANA specialist's local build and user-run deployment handoff, including `cds build --for hana` when applicable. Automate every safe step exposed by the live MCP tools: inspect, analyze, delegate, validate, collect evidence, iterate on findings, and prepare release readiness. Keep all state-changing gates explicit and authorized.
|
|
26
26
|
7. **Plan delegation to implementation agents.** When implementation is requested, break the work into safe, ordered work packages. If the Copilot client supports subagents/delegation, hand off implementation packages to the specialized agents; otherwise provide ready-to-paste briefs for:
|
|
27
27
|
- **ABAP Developer** for ABAP classes, reports, interfaces, tests, quality checks, and transport preparation.
|
|
28
28
|
- **RAP Service Developer** for RAP BOs, CDS/projections, behavior, service definitions, bindings, and OData validation.
|
|
@@ -12,7 +12,7 @@ Query the active MCP server's live `tools/list` once; use task-relevant tools wi
|
|
|
12
12
|
1. Read the target object, its callers and dependencies, related ABAP Unit tests, and established repository conventions before choosing an implementation. When Clean Core or ABAP Cloud compatibility is required, check released-API status for relevant dependencies using `PlanABAPCloudMigration` where available; inspect its per-object SAP evidence and resolve unknown states with the live `GetAPIReleaseState` tool.
|
|
13
13
|
2. Plan before implementing. Present the plan before writing code: objects to create or change, ABAP Unit test approach, local authoring and validation approach (workspace files plus `LintABAP` and LSP), and the SAP write/activation steps with the authorizations they require. Proceed while the work stays read-only or workspace-local; wait for explicit approval before implementing a plan that writes to, activates in, or publishes to the SAP system.
|
|
14
14
|
3. Translate the request into observable behavior. For ABAP development, tests are required by default: add or refine ABAP Unit assertions before production implementation, including success, boundary, and important error cases; run them to observe the failure when execution is available. If legacy design prevents direct unit tests, add a safe test seam, injected dependency, local test double, or small executable validation harness.
|
|
15
|
-
4. Author locally first; check and lint before sending to SAP. Create or change sources in the workspace as
|
|
15
|
+
4. Author locally first; check and lint before sending to SAP. Create or change sources in the workspace as plain source files named `object.type.extension` (for example `zcl_report.clas.abap`), together with their tests, instead of writing directly into SAP. Run the live `LintABAP` tool on those caller-supplied local files plus every required dependency source, and consume BAS editor LSP diagnostics when configured; `LintABAP` analyzes only the submitted in-memory files. Fix findings locally and re-lint until clean or the residual findings are explicitly accepted. Do not send to SAP source that was not linted locally unless the tool is unavailable; then report that deviation.
|
|
16
16
|
5. Make the smallest implementation that meets that behavior. Send the locally authored, locally linted sources to SAP using the live source-edit tool schemas: `EditSource` for a localized change and `WriteSource` for a larger rewrite. Keep source changes within the user-authorized target and preserve existing object conventions.
|
|
17
17
|
For authorized changes across multiple full-source objects, stage exact `GetSource` and `WriteSource` arguments with `PrepareABAPChangeSet`, review every returned diff, then call `ApplyABAPChangeSet`. The apply tool rechecks source before each write and reports partial results; it does not provide rollback. For single-object sends, prefer the source-hash guard (`include_hash` on reads, `expected_source_hash` on writes) when the live schemas offer it.
|
|
18
18
|
6. Self-validate the changed public contract. For interfaces, APIs, reports, function modules, and wrappers, execute the changed entry point in BAS or SAP using representative input. Prefer safe read-only real data from the target system; otherwise create isolated sample data only when authorized and clean it up where possible. Verify the actual output, side effects, and at least one empty/negative path.
|
|
@@ -13,7 +13,7 @@ Query the active MCP server's live `tools/list` once, then use only task-relevan
|
|
|
13
13
|
- For a behavior change, create or refine a real ABAP Unit assertion first, run it to observe the regression, then rerun after implementation. If an executable red/green cycle is unavailable, state the concrete constraint and the strongest check actually performed; never report a substitute as a passing test.
|
|
14
14
|
- Require runtime validation for changed contracts. Execute the changed class/interface/function/report/service/CDS path with representative data in BAS or SAP. Prefer non-destructive real data from the target system; use authorized isolated test/sample data only when real data is unsafe or unavailable. Verify expected results plus a negative, empty, or boundary scenario.
|
|
15
15
|
- When validating interfaces, integrations, or CDS views, prove the artifact can be consumed: instantiate/call the implementing class, invoke the API/RFC/OData/RAP action/query, or query the CDS entity with `RunQuery`/`GetTableContents` as appropriate. Inspect returned structures/messages/rows rather than only checking compilation.
|
|
16
|
-
- Run the live `LintABAP` MCP tool on all relevant caller-supplied source files, using
|
|
16
|
+
- Run the live `LintABAP` MCP tool on all relevant caller-supplied source files, using `object.type.extension` filenames and including required dependency sources. The tool analyzes only the files supplied to it; it does not read the workspace or resolve dependencies.
|
|
17
17
|
- Consume BAS editor LSP diagnostics when configured. `vsp lsp --stdio` is editor LSP functionality, not an MCP tool and not an entry in MCP `tools/list`.
|
|
18
18
|
- Use SAP `SyntaxCheck`, `RunUnitTests`, and `RunATCCheck` when exposed by the live tool listing and permitted for the task. Check pending inactive objects with `GetInactiveObjects`, then activate authorized SAP object changes in dependency order with `Activate` / `ActivateMultiple`. Address findings and rerun affected checks. Do not suppress findings silently or present unavailable/skipped checks as passes.
|
|
19
19
|
- Report ABAP Unit, runtime validation data source, activation, lint, LSP, syntax, and ATC outcomes separately, including exact reasons for checks not run.
|
|
@@ -11,7 +11,7 @@ Query the active MCP server's live `tools/list` once; use task-relevant tools wi
|
|
|
11
11
|
|
|
12
12
|
1. Inspect target-system release and capabilities with live `GetSystemInfo` and `GetFeatures` tools when available; inspect existing RAP objects and package conventions before design. Confirm RAP support and each required operation from the active MCP `tools/list`, not from static documentation.
|
|
13
13
|
2. Plan before implementing. Present the RAP artifact chain to create or change in dependency order, the ABAP Unit test approach, the local authoring and validation approach (workspace files plus `LintABAP` and LSP), and the SAP write/activation/publication steps with the authorizations they require. Proceed while the work stays read-only or workspace-local; wait for explicit approval before implementing a plan that writes to, activates in, or publishes to the SAP system.
|
|
14
|
-
3. Author locally first; check and lint before sending to SAP. Create or change the RAP artifacts in the workspace as
|
|
14
|
+
3. Author locally first; check and lint before sending to SAP. Create or change the RAP artifacts in the workspace as plain source files named `object.type.extension` (for example `zrap_i_booking.ddls.asddls` or `zrap_bp_booking.clas.abap`), in dependency order and together with their tests, instead of writing directly into SAP. Run the live `LintABAP` tool on those caller-supplied local files plus every required dependency source, and consume BAS editor LSP diagnostics when configured; `LintABAP` analyzes only the submitted in-memory files. Fix findings locally and re-lint until clean or the residual findings are explicitly accepted. Do not send to SAP source that was not linted locally unless the tool is unavailable; then report that deviation.
|
|
15
15
|
4. Build the requested model through the required dependencies: CDS entities, behavior definitions and implementations, projections, service definitions, and service bindings. Follow existing object patterns and validate objects in dependency order with operations actually exposed by the system. Add or update ABAP Unit tests for behavior implementations, validations, determinations, actions, and authorization-relevant logic where applicable.
|
|
16
16
|
5. Send the locally authored, locally linted artifacts to SAP with the same write and change-set tools (CDS sources, behavior definitions, and service definitions included) and self-validate RAP behavior end-to-end against the live system. Exercise read/query paths and any changed create/update/delete/action behavior using representative data. Prefer safe read-only real data for query validation; create isolated sample data only when the task authorizes mutation and clean it up when possible. Verify response payloads, messages, failed validations, and at least one negative/empty scenario.
|
|
17
17
|
6. Check pending inactive objects with `GetInactiveObjects`, then activate authorized RAP object changes in dependency order. If service publication is needed, state the external handoff because this addon does not expose publish/unpublish tools. Validate the service end-to-end using the live tools available for that system; report actual activation, publication handoff, and runtime state.
|
|
@@ -12,7 +12,7 @@ Query the active MCP server's live `tools/list` once and use the exact live name
|
|
|
12
12
|
1. Confirm target destination, package, transport/temporary target, RAP object names, and whether activation/publication/data mutation is authorized. Transport release and deletion remain out of scope for this skill; prepare and report transport context only.
|
|
13
13
|
2. Plan before implementing. Present the RAP object chain to create or change in dependency order, the ABAP Unit test approach, the local authoring and validation approach (workspace files plus `LintABAP` and LSP), and the SAP write/activation/publication steps with the authorizations they require. Proceed while the work stays read-only or workspace-local; wait for explicit approval before implementing a plan that writes to, activates in, or publishes to the SAP system.
|
|
14
14
|
3. Inspect the full RAP object chain before changing anything: interface/root CDS, child/composition CDS, behavior definition, behavior pool/local handlers, projection CDS, projection behavior, service definition, service binding, tests, and package conventions.
|
|
15
|
-
4. Author locally first; check and lint before sending to SAP. Create or change the RAP artifacts in the workspace as
|
|
15
|
+
4. Author locally first; check and lint before sending to SAP. Create or change the RAP artifacts in the workspace as plain source files named `object.type.extension` (for example `zrap_i_booking.ddls.asddls` or `zrap_bp_booking.clas.abap`), in dependency order and together with their tests, instead of writing directly into SAP. Run the live `LintABAP` tool on those caller-supplied local files plus every required dependency source, and consume BAS editor LSP diagnostics when configured; `LintABAP` analyzes only the submitted in-memory files. Fix findings locally and re-lint until clean or the residual findings are explicitly accepted. Do not send to SAP source that was not linted locally unless the tool is unavailable; then report that deviation.
|
|
16
16
|
5. Validate behavior implementations with tests first where feasible. Cover validations, determinations, actions, feature control, numbering, authorization-relevant branches, reported/failed messages, and negative or empty paths.
|
|
17
17
|
6. Validate runtime service behavior end-to-end after the locally linted sources were sent to SAP. Use read-only `RunQuery`/`GetTableContents` for query paths. For mutating behavior, proceed only when authorized, use isolated data, verify persisted state/messages, and clean up when possible.
|
|
18
18
|
Generate a reusable GET-only OData smoke suite with `GenerateRAPRegressionSuite`, save its returned JSON with the change, and rerun it with `RunRAPRegressionSuite`. Add JSON-path assertions for business behavior that metadata alone cannot prove.
|
|
@@ -10,7 +10,7 @@ description: Orchestrate the SAP delivery lifecycle from requirement analysis th
|
|
|
10
10
|
Query the active MCP server's live `tools/list` once; use task-relevant tools with their returned schemas. Investigate system and repository context with `GetSystemInfo`, `GetFeatures`, `GetConnectionInfo`, `GetInstalledComponents`, `SearchObject`, `GrepObjects`, `GrepPackages`, `GetSource`, `GetContext`, `FindDefinition`, `FindReferences`, `CompareSource`, and dependency tools (`ListDependencies`, `GetCDSDependencies`, `GetCDSImpactAnalysis`). Validate with `LintABAP`, `SyntaxCheck`, `RunUnitTests`, `RunATCCheck`, runtime queries (`RunQuery`, `GetTableContents`), `GetApplicationLog`, and `GenerateRAPRegressionSuite` / `RunRAPRegressionSuite` when exposed. Use transport tools for readiness planning only; never claim transport release/deletion support because transport release is unavailable through this addon. Tool names shown in PascalCase (such as `GetSource` or `LintABAP`) are logical names; the live MCP surface exposes them as lowercase snake_case. A server scoped to a single destination (the normal case for generated `mcp.json` entries) exposes them unprefixed, so `GetSource` appears as `get_source` and `LintABAP` as `lint_abap`; when several destinations share one server, each name is destination-prefixed with its slug for disambiguation (for example `cfd_run_query`). Always call the exact names returned by `tools/list`.
|
|
11
11
|
|
|
12
12
|
1. Convert the requirement into an SDLC plan: discovery, fit-gap/API recommendation, architecture, implementation packages, test strategy, validation gates, activation/publication gates, transport readiness, deployment notes, rollback, and operations handover.
|
|
13
|
-
2. Require a planning phase before implementation for every work package. Each package starts with a presented plan of objects, tests, and validation steps; no SAP state-changing package proceeds before the user approves it. Every implementation package includes a local quality gate: sources are authored locally as
|
|
13
|
+
2. Require a planning phase before implementation for every work package. Each package starts with a presented plan of objects, tests, and validation steps; no SAP state-changing package proceeds before the user approves it. Every implementation package includes a local quality gate: sources are authored locally as plain workspace source files (`object.type.extension`) and must pass `LintABAP` plus LSP checks before they are sent to the SAP system.
|
|
14
14
|
3. Automate what the active MCP tools safely allow. Read and analyze the system, propose standard APIs and extension points, generate implementation briefs, request or delegate coding work, run checks, collect evidence, and iterate until acceptance criteria are met or a blocker is proven.
|
|
15
15
|
4. Keep state-changing gates explicit. Do not edit source, activate, publish, create transports, create test data, or execute mutating scenarios unless the user authorized that phase and the target destination/package/transport context is known.
|
|
16
16
|
5. Enforce quality gates: unit or executable behavior tests, syntax, lint, ATC where available, runtime validation, security/authorization review, Clean Core/released API check, transport dependency check, and documented skipped checks with reasons.
|
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ Use the attached destination-prefixed tools directly in chat. Do not launch `sap
|
|
|
84
84
|
|
|
85
85
|
**Included skills:** `abap-development` · `abap-testing-quality` · `cds-development` · `rap-development` · `abap-debugging` · `abap-runtime-analysis` · `rap-service-delivery` · `sap-standard-api-analysis` · `clean-core-extensibility` · `sap-sdlc-orchestration` · `sap-transport-release` · `hana-cloud-inspection` · `hana-cloud-native-development` · `hana-cloud-validation`
|
|
86
86
|
|
|
87
|
-
Agents and skills install
|
|
87
|
+
Agents and skills install for the harnesses you pick during global installation: a multi-select offers GitHub Copilot and Claude Code pre-checked, plus OpenAI Codex, Cursor, Gemini CLI, and opencode. In non-interactive installs they are installed for GitHub Copilot only. Repository-scoped installation instructions appear below.
|
|
88
88
|
|
|
89
89
|
## ✨ Your SAP development cockpit, inside Copilot Chat
|
|
90
90
|
|
|
@@ -278,7 +278,7 @@ The installer handles VSP provisioning automatically:
|
|
|
278
278
|
With `H2O_URL` set, an interactive install opens a checkbox picker with no destinations selected by default. Use **Space** to choose destinations and **Enter** to confirm. Press **a** to toggle all destinations (select all if any are unchecked; otherwise clear the selection). Confirming with none selected removes this add-on's managed MCP entries. When the `cf` CLI 8.18 or newer is authenticated to a targeted space, setup first offers an optional import from that space's Destination service; type **y** then **Enter** to include it, or press **Enter** to skip. Accepted CF and BAS destinations appear together in the picker. npm may run its install hook without an interactive terminal, even when the shell is interactive; in that case, selection is skipped without changing MCP config.
|
|
279
279
|
|
|
280
280
|
|
|
281
|
-
The same postinstall
|
|
281
|
+
The same postinstall offers the bundled agents and skills for several AI coding harnesses in a checkbox picker. **GitHub Copilot** and **Claude Code** start pre-checked, so pressing **Enter** alone installs both; **Space** selects or deselects a harness, **a** toggles all six, and confirming with none selected skips the step without changing files. With no interactive terminal, the assets are installed for GitHub Copilot only; set `SAP_AI_DEV_TOOLKIT_HARNESSES` to a comma-separated list of harness ids (`github-copilot`, `claude-code`, `codex`, `cursor`, `gemini-cli`, `opencode`) to choose non-interactively.
|
|
282
282
|
|
|
283
283
|
Some current npm versions also require install hooks to be approved. If npm reports that `sap-ai-dev-toolkit`'s `postinstall` was blocked, allow it during a fresh install with `npm install --global --allow-scripts=sap-ai-dev-toolkit sap-ai-dev-toolkit`, or run `sap-ai-dev --setup` from an interactive BAS terminal after installation.
|
|
284
284
|
|
|
@@ -325,7 +325,7 @@ The package includes five user-invocable custom agents (**SAP Solution Architect
|
|
|
325
325
|
2. **Understand the request.** Establish the expected behavior and, for SAP changes, the destination, package, and transport or temporary target. Ask only when a material detail is missing.
|
|
326
326
|
3. **Plan before implementing.** Every implementation task starts with a presented plan: objects to change, ABAP Unit test approach, local authoring/validation, and the SAP write/activation steps it requires. The agents proceed while work stays read-only or workspace-local and wait for explicit approval before any plan that writes to, activates in, or publishes to the SAP system.
|
|
327
327
|
4. **Inspect before editing.** Read relevant source, tests, callers, dependencies, standard APIs, release state, and conventions; query the active MCP server's live `tools/list` and use its exact destination-prefixed tools and schemas.
|
|
328
|
-
5. **Implement with behavior in mind, locally first.** Add or refine an ABAP Unit assertion first when an executable regression test is available, then make the smallest change that meets the request. Sources are authored locally as
|
|
328
|
+
5. **Implement with behavior in mind, locally first.** Add or refine an ABAP Unit assertion first when an executable regression test is available, then make the smallest change that meets the request. Sources are authored locally as plain workspace source files (`object.type.extension`), and checked and linted there before they are sent to SAP.
|
|
329
329
|
6. **Check and lint before sending to SAP.** Run the local `LintABAP` tool on the caller-supplied files plus required dependencies and consume BAS editor LSP diagnostics when configured; fix findings locally and re-lint. Only then transfer the sources to SAP and use the remote `SyntaxCheck`, `RunUnitTests`, and `RunATCCheck` when exposed and relevant. Lint and syntax checks do not replace behavior tests.
|
|
330
330
|
7. **Protect SAP state.** Only make requested changes. Activate objects or publish services only when asked; create transports only when explicitly authorized. Release and deletion of transports are unavailable through this add-on.
|
|
331
331
|
8. **Report observed results.** Summarize architecture decisions, changed objects, implementation handoff or actual validation, activation, and publication outcomes. Identify skipped checks and exact blockers; never claim a check passed if it did not run.
|
|
@@ -351,11 +351,22 @@ The package includes five user-invocable custom agents (**SAP Solution Architect
|
|
|
351
351
|
|
|
352
352
|
### 📥 Install for your BAS user
|
|
353
353
|
|
|
354
|
-
After destination setup, the installer prints a 🤖 notice
|
|
354
|
+
After destination setup, the installer prints a 🤖 notice and a checkbox picker asking which AI coding harnesses should receive the bundled agents and skills. **Space** selects or deselects, **a** toggles all, **Enter** confirms. **GitHub Copilot** and **Claude Code** are pre-checked, so pressing **Enter** without changes installs both; confirming with none selected skips the step and leaves your files unchanged.
|
|
355
355
|
|
|
356
|
-
|
|
356
|
+
| Harness | Skills land in | Agents land in |
|
|
357
|
+
| --- | --- | --- |
|
|
358
|
+
| GitHub Copilot | `$HOME/.copilot/skills/` | `$HOME/.copilot/agents/*.agent.md` |
|
|
359
|
+
| Claude Code | `$HOME/.claude/skills/` | `$HOME/.claude/agents/<id>.md` (frontmatter reduced to `name` and `description`) |
|
|
360
|
+
| OpenAI Codex | `$HOME/.agents/skills/` | — |
|
|
361
|
+
| Cursor | `$HOME/.cursor/skills/` | — |
|
|
362
|
+
| Gemini CLI | `$HOME/.gemini/skills/` | — |
|
|
363
|
+
| opencode | `$XDG_CONFIG_HOME/opencode/skills/` (default `$HOME/.config/opencode/skills/`) | `$XDG_CONFIG_HOME/opencode/agents/<id>.md` (as subagents) |
|
|
364
|
+
|
|
365
|
+
All 14 skills use the portable `SKILL.md` format and are installed verbatim. The 5 agents use Copilot's `.agent.md` format, so they are only installed for harnesses with a custom-agent mechanism (GitHub Copilot, Claude Code, opencode), with frontmatter adapted where needed. Selecting several harnesses duplicates the small markdown payloads on disk.
|
|
366
|
+
|
|
367
|
+
These user-level customizations are available across workspaces opened by the same BAS user in the same dev space. The harness must be available in BAS and may need a window reload to discover new files. A non-interactive install skips the picker and installs for GitHub Copilot only; override with `SAP_AI_DEV_TOOLKIT_HARNESSES` (comma-separated harness ids). `npm install --ignore-scripts` skips the postinstall wizard entirely.
|
|
357
368
|
|
|
358
|
-
On reinstall or package upgrade, unchanged add-on-managed files are updated. Existing customizations and files edited since the previous install are preserved; postinstall reports paths that need manual review instead of overwriting them.
|
|
369
|
+
On reinstall or package upgrade, unchanged add-on-managed files are updated per harness. Existing customizations and files edited since the previous install are preserved; postinstall reports paths that need manual review instead of overwriting them.
|
|
359
370
|
|
|
360
371
|
### 🗂️ Add the agents and skills to a repository
|
|
361
372
|
|
|
@@ -508,7 +519,7 @@ The menu includes the tools registered by the active VSP mode, the `GetApplicati
|
|
|
508
519
|
|
|
509
520
|
### 🧹 Lint submitted ABAP source locally
|
|
510
521
|
|
|
511
|
-
The tool is named `lint_abap` (the local workflow name `LintABAP` exposed in snake_case; on a multi-destination server it is `<destination-slug>_lint_abap`). It accepts caller-supplied
|
|
522
|
+
The tool is named `lint_abap` (the local workflow name `LintABAP` exposed in snake_case; on a multi-destination server it is `<destination-slug>_lint_abap`). It accepts caller-supplied source files with `object.type.extension` filenames; config is optional and, when present, is the full abaplint configuration rather than a merge with defaults.
|
|
512
523
|
|
|
513
524
|
```json
|
|
514
525
|
{
|
|
@@ -544,7 +555,7 @@ For a transport report, include exactly one `GetTransport` check whose `transpor
|
|
|
544
555
|
|
|
545
556
|
For an ABAP Cloud assessment, get object URIs from `SearchObject` and pass them to `PlanABAPCloudMigration`. It reports release evidence; it does not guess replacement APIs. Save the JSON from `GenerateRAPRegressionSuite` and pass it to `RunRAPRegressionSuite` after relevant service changes.
|
|
546
557
|
|
|
547
|
-
`sap-ai-dev --doctor` probes each discovered destination, checks that each managed MCP entry still matches its selected BAS destination, starts VSP, lists MCP tools, and
|
|
558
|
+
`sap-ai-dev --doctor` probes each discovered destination, checks that each managed MCP entry still matches its selected BAS destination, starts VSP, lists MCP tools, and calls `GetSystemInfo` directly when that tool is exposed. It repairs only tagged toolkit-owned BAS entries that can be verified against complete destination discovery; it does not add destinations, remove stale or third-party entries, or overwrite malformed configuration. `--doctor --json` reports redacted diagnostics and exits unsuccessfully when a required check fails.
|
|
548
559
|
|
|
549
560
|
`tools/list` confirms what this MCP server exposes; it does not prove that VS Code attached the server to the active chat. Start the selected destination in **MCP: List Servers**, enable it in the Chat tools picker, and reload/reselect the agent if the host binding is stale. This add-on cannot inspect or repair another MCP server's registration or a host-wide tool budget.
|
|
550
561
|
|
|
@@ -607,7 +618,7 @@ The write and activation tools change SAP state. Confirm the target, package, an
|
|
|
607
618
|
| `RunATCCheck` | Run an ABAP Test Cockpit check and return findings. |
|
|
608
619
|
| `GetSystemInfo` | Read system ID, SAP release, kernel, and database details. |
|
|
609
620
|
| `GetInstalledComponents` | List installed software components and versions. |
|
|
610
|
-
| `GetFeatures` | Probe optional system capabilities, including
|
|
621
|
+
| `GetFeatures` | Probe optional system capabilities, including RAP/OData, AMDP debugging, UI5/BSP, and CTS transports. |
|
|
611
622
|
| `GetConnectionInfo` | Show the connected user, client, URL, mode, and feature-probe summary for the current destination session. |
|
|
612
623
|
| `PrettyPrint` | Format ABAP source text without saving it to SAP. |
|
|
613
624
|
|
|
@@ -738,9 +749,7 @@ The response contains a `tools` array. A `RunQuery` entry resembles this excerpt
|
|
|
738
749
|
| `SAP_AI_DEV_TOOLKIT_DESTINATION` | Comma-separated destination allowlist for normal runtime discovery. Setup clears this temporarily so it can display all eligible systems. |
|
|
739
750
|
| `SAP_AI_DEV_TOOLKIT_MODE` | VSP child mode (`expert` by default; `focused` omits `ActivateMultiple`, `GetUserTransports`, and `GetTransportInfo`). The proxy exposes its curated tool subset and local workflow tools, including `LintABAP`. |
|
|
740
751
|
| `SAP_ALLOW_TRANSPORTABLE_EDITS` | Generated MCP entries set this to `true` to permit source edits in transportable packages; VSP safety checks and SAP authorizations still apply. |
|
|
741
|
-
| `
|
|
742
|
-
| `SAP_AI_DEV_TOOLKIT_HTTP_PROXY` | Egress proxy for relay and discovery traffic. Takes precedence over `HTTP_PROXY`/`http_proxy`; unset means the default BAS proxy for `.dest` hosts, empty means direct. |
|
|
743
|
-
| `SAP_AI_DEV_TOOLKIT_MAX_CSRF_RETRIES` | Bounded retries after explicit CSRF-session rejection per unsafe request (default 3, maximum 10). Invalid values use the default. |
|
|
752
|
+
| `SAP_AI_DEV_TOOLKIT_HTTP_PROXY` | Egress proxy for BAS discovery and probe traffic. Takes precedence over `HTTP_PROXY`/`http_proxy`; unset means the default BAS proxy for `.dest` hosts, empty means direct. |
|
|
744
753
|
| `SAP_AI_DEV_TOOLKIT_REQUEST_TIMEOUT_MS` | Per-request timeout for calls forwarded to a VSP child (default 600000 = 10 minutes; `0` disables). A stalled request fails with a timeout error; the child is left running. |
|
|
745
754
|
| `SAP_AI_DEV_MCP_CONFIG` | Explicit MCP configuration path; highest precedence. |
|
|
746
755
|
| `SAP_AI_DEV_TOOLKIT_MCP_CONFIG` | Branded compatibility alias for the MCP configuration path. |
|
|
@@ -757,18 +766,13 @@ The response contains a `tools` array. A `RunQuery` entry resembles this excerpt
|
|
|
757
766
|
|
|
758
767
|
#### Self-healing modes
|
|
759
768
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
1. **CSRF session pairing** — a scoped cookie jar retains session cookies received during safe reads and token fetches, applies updates and expirations, and replays the matching token/cookie pair on POST/PUT/PATCH/DELETE. Tokens are session-scoped and always fetched from the GET-capable `/sap/bc/adt/discovery` endpoint, so unsafe requests to POST-only services (ADT data preview) are covered too. If no token can be obtained at all, the relay fails closed and does not send the unsafe request. A token that arrives without session cookies is still used — BAS proxy routes strip `Set-Cookie`, yet the backend accepts the token on the proxy-established Basic-auth session — and a CSRF rejection on such a cookie-less route is surfaced without pointless retries.
|
|
763
|
-
2. **Session refresh** — when SAP explicitly rejects a CSRF token, the cached session is dropped, a fresh token+cookie pair is fetched, and the request is retried within the configured bound. Generic authorization 401/403 responses are returned without being mislabeled or retried as CSRF failures.
|
|
764
|
-
3. **Proxy tunnel fallback** — when the BAS proxy refuses absolute-form requests (502/504 or transport errors), the relay switches to a CONNECT tunnel through the same proxy and keeps going.
|
|
765
|
-
4. **Child crash recovery** — a crashed VSP child is restarted transparently, re-initialized, tools re-registered, and the interrupted `tools/call` retried once before any error reaches the client.
|
|
766
|
-
5. **Configured destination authentication** — BAS destination children use the selected BAS destination as their only SAP identity. The toolkit clears VSP local authentication environment values so a project `.env` cannot override or conflict with destination authentication. Setup does not prompt for or store SAP usernames/passwords, and the relay does not connect directly to a backend host as a credential fallback. Cloud Foundry destinations use only credentials returned by their configured Destination service record.
|
|
769
|
+
VSP children connect to each configured destination URL directly. BAS destination authentication remains selected through VSP's `--proxy-auth` path; Cloud Foundry on-premise destinations continue through the separate Connectivity proxy.
|
|
770
|
+
Child crash recovery restarts a failed VSP child, re-initializes it, re-registers tools, and retries the interrupted `tools/call` once before surfacing an error. State-changing tools are not retried.
|
|
767
771
|
Existing installs that still set the previous `BAS_VSP_*` environment variables remain supported. The setup wizard writes new MCP entries with the `SAP_AI_DEV_TOOLKIT_*` names.
|
|
768
772
|
|
|
769
773
|
#### Server logging
|
|
770
774
|
|
|
771
|
-
Every observable event — startup banner (version, PID, VSP binary), destination discovery,
|
|
775
|
+
Every observable event — startup banner (version, PID, VSP binary), destination discovery, VSP session initialization, each JSON-RPC request with its id, every tool call with duration and outcome, self-healing restarts, and errors — is logged twice: to **stderr** with ISO timestamps (visible when the server runs in a terminal or in hosts that surface stderr), and to the host's **MCP server output channel** as standard `notifications/message` log notifications (VS Code/BAS and Claude Code render these under the server's output). Log notifications start at `info`; send `logging/setLevel` with `debug` (or higher) from the client to adjust what reaches the channel — stderr always receives everything. Credentials, cookies, and tokens are redacted.
|
|
772
776
|
|
|
773
777
|
<a id="troubleshooting"></a>
|
|
774
778
|
|
|
@@ -785,6 +789,8 @@ Next, check that the backend answers at `/sap/bc/adt`.
|
|
|
785
789
|
|
|
786
790
|
Runtime diagnostics take the stderr lane. In the MCP server's Output view, check per-destination ADT probe status, VSP child stderr, and failed tool-call details; stdout is reserved for MCP protocol messages.
|
|
787
791
|
|
|
792
|
+
If `GetSystemInfo` or `RunQuery` reports a CSRF-token fetch failure, VSP tries `/sap/bc/adt/core/discovery` first and falls back to `/sap/bc/adt/discovery` when the core endpoint returns HTTP 404 or 5xx without a token. If both endpoints fail, the error includes their HTTP statuses; check BAS destination routing and the SAP ADT service. `GetInstalledComponents` is a separate GET request and a failure there is not evidence of a CSRF problem.
|
|
793
|
+
|
|
788
794
|
If automatic Go or VSP provisioning fails, check network access and the package's supported platform. You can install Go manually or set `GO_BINARY` as an explicit fallback. A trusted prebuilt VSP can be supplied with `SAP_AI_DEV_TOOLKIT_BINARY`.
|
|
789
795
|
|
|
790
796
|
## 🧑💻 Development
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sap-ai-dev-toolkit",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "SAP AI development toolkit for BAS, ABAP, RAP, CAP, HANA Cloud, Fiori, UI5, and MCP",
|
|
5
5
|
"author": "Gurkan Yilmaz",
|
|
6
6
|
"type": "module",
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"@modelcontextprotocol/sdk": "1.30.1",
|
|
13
13
|
"@sap/hana-client": "2.30.27",
|
|
14
14
|
"terminal-kit": "^3.1.4",
|
|
15
|
-
"tough-cookie": "^5.1.2",
|
|
16
15
|
"undici": "^6.21.3",
|
|
17
16
|
"zod": "4.6.5"
|
|
18
17
|
},
|
|
@@ -308,3 +308,128 @@ diff --git a/internal/mcp/tools_register.go b/internal/mcp/tools_register.go
|
|
|
308
308
|
+ }),
|
|
309
309
|
),
|
|
310
310
|
), s.handleActivateMultiple)
|
|
311
|
+
}
|
|
312
|
+
diff --git a/pkg/adt/http.go b/pkg/adt/http.go
|
|
313
|
+
--- a/pkg/adt/http.go
|
|
314
|
+
+++ b/pkg/adt/http.go
|
|
315
|
+
@@ -499,6 +499,7 @@ func (t *Transport) fetchCSRFTokenFor(ctx context.Context, stateful bool) error
|
|
316
|
+
// allowReauth exists to break a cycle: re-authenticating ends with a token
|
|
317
|
+
// fetch of its own, and that fetch must not start another re-authentication.
|
|
318
|
+
func (t *Transport) fetchCSRFTokenWithReauth(ctx context.Context, allowReauth bool, stateful bool) error {
|
|
319
|
+
+ var alternateFailure string
|
|
320
|
+
token, status, redirected, err := t.probeCSRFToken(ctx, http.MethodHead, stateful)
|
|
321
|
+
if err != nil {
|
|
322
|
+
return err
|
|
323
|
+
@@ -513,6 +514,29 @@ func (t *Transport) fetchCSRFTokenWithReauth(ctx context.Context, allowReauth bo
|
|
324
|
+
if err != nil {
|
|
325
|
+
return err
|
|
326
|
+
}
|
|
327
|
+
+ // Some ADT installations return 500 from /core/discovery even though
|
|
328
|
+
+ // the standard /discovery endpoint can issue a token. Keep the
|
|
329
|
+
+ // existing fast path, but do not make every POST depend on the
|
|
330
|
+
+ // failing endpoint.
|
|
331
|
+
+ if !isCSRFToken(token) && (getStatus >= 500 || getStatus == http.StatusNotFound) {
|
|
332
|
+
+ coreStatus := getStatus
|
|
333
|
+
+ token, status, redirected, err = t.probeCSRFTokenAt(ctx, http.MethodHead, stateful, "/sap/bc/adt/discovery")
|
|
334
|
+
+ if err != nil {
|
|
335
|
+
+ return err
|
|
336
|
+
+ }
|
|
337
|
+
+ if !isCSRFToken(token) {
|
|
338
|
+
+ if status == http.StatusUnauthorized && !t.canReauth() {
|
|
339
|
+
+ return fmt.Errorf("authentication failed (401): check username/password")
|
|
340
|
+
+ }
|
|
341
|
+
+ token, getStatus, getRedirected, err = t.probeCSRFTokenAt(ctx, http.MethodGet, stateful, "/sap/bc/adt/discovery")
|
|
342
|
+
+ if err != nil {
|
|
343
|
+
+ return err
|
|
344
|
+
+ }
|
|
345
|
+
+ if !isCSRFToken(token) {
|
|
346
|
+
+ alternateFailure = fmt.Sprintf("no CSRF token from /sap/bc/adt/core/discovery (GET %d) or /sap/bc/adt/discovery (HEAD %d, GET %d)", coreStatus, status, getStatus)
|
|
347
|
+
+ }
|
|
348
|
+
+ }
|
|
349
|
+
+ }
|
|
350
|
+
if !isCSRFToken(token) {
|
|
351
|
+
// An expired SSO session rarely announces itself as a 401. ICF sends
|
|
352
|
+
// the request on to the identity provider, the redirect chain is
|
|
353
|
+
@@ -537,6 +561,9 @@ func (t *Transport) fetchCSRFTokenWithReauth(ctx context.Context, allowReauth bo
|
|
354
|
+
}
|
|
355
|
+
return t.fetchCSRFTokenWithReauth(ctx, false, stateful)
|
|
356
|
+
}
|
|
357
|
+
+ if alternateFailure != "" {
|
|
358
|
+
+ return errors.New(alternateFailure)
|
|
359
|
+
+ }
|
|
360
|
+
switch getStatus {
|
|
361
|
+
case http.StatusUnauthorized:
|
|
362
|
+
return fmt.Errorf("authentication failed (401): check username/password")
|
|
363
|
+
@@ -556,7 +583,11 @@ func (t *Transport) fetchCSRFTokenWithReauth(ctx context.Context, allowReauth bo
|
|
364
|
+
// returns the token (empty when the server did not supply one), the status, and
|
|
365
|
+
// whether the answer came from somewhere other than the SAP host.
|
|
366
|
+
func (t *Transport) probeCSRFToken(ctx context.Context, method string, stateful bool) (token string, status int, redirected bool, err error) {
|
|
367
|
+
- reqURL, err := t.buildURL("/sap/bc/adt/core/discovery", nil)
|
|
368
|
+
+ return t.probeCSRFTokenAt(ctx, method, stateful, "/sap/bc/adt/core/discovery")
|
|
369
|
+
+}
|
|
370
|
+
+
|
|
371
|
+
+func (t *Transport) probeCSRFTokenAt(ctx context.Context, method string, stateful bool, path string) (token string, status int, redirected bool, err error) {
|
|
372
|
+
+ reqURL, err := t.buildURL(path, nil)
|
|
373
|
+
if err != nil {
|
|
374
|
+
return "", 0, false, fmt.Errorf("building URL: %w", err)
|
|
375
|
+
}
|
|
376
|
+
diff --git a/pkg/adt/http_csrf_discovery_test.go b/pkg/adt/http_csrf_discovery_test.go
|
|
377
|
+
new file mode 100644
|
|
378
|
+
--- /dev/null
|
|
379
|
+
+++ b/pkg/adt/http_csrf_discovery_test.go
|
|
380
|
+
@@ -0,0 +1,55 @@
|
|
381
|
+
+package adt
|
|
382
|
+
+
|
|
383
|
+
+import (
|
|
384
|
+
+ "context"
|
|
385
|
+
+ "net/http"
|
|
386
|
+
+ "net/http/httptest"
|
|
387
|
+
+ "strings"
|
|
388
|
+
+ "testing"
|
|
389
|
+
+)
|
|
390
|
+
+
|
|
391
|
+
+func TestCSRFAlternateDiscoveryOnCoreFailure(t *testing.T) {
|
|
392
|
+
+ var requests []string
|
|
393
|
+
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
394
|
+
+ requests = append(requests, r.Method+" "+r.URL.Path)
|
|
395
|
+
+ switch r.URL.Path {
|
|
396
|
+
+ case "/sap/bc/adt/core/discovery":
|
|
397
|
+
+ w.WriteHeader(http.StatusInternalServerError)
|
|
398
|
+
+ case "/sap/bc/adt/discovery":
|
|
399
|
+
+ w.Header().Set("X-CSRF-Token", "valid-token")
|
|
400
|
+
+ case "/sap/bc/adt/datapreview/freestyle":
|
|
401
|
+
+ if r.Header.Get("X-CSRF-Token") != "valid-token" {
|
|
402
|
+
+ t.Errorf("POST missing fetched token")
|
|
403
|
+
+ w.WriteHeader(http.StatusForbidden)
|
|
404
|
+
+ }
|
|
405
|
+
+ default:
|
|
406
|
+
+ t.Errorf("unexpected request %s", r.URL.Path)
|
|
407
|
+
+ }
|
|
408
|
+
+ }))
|
|
409
|
+
+ defer srv.Close()
|
|
410
|
+
+ tr := newCSRFTestTransport(t, srv)
|
|
411
|
+
+ _, err := tr.Request(context.Background(), "/sap/bc/adt/datapreview/freestyle", &RequestOptions{Method: http.MethodPost, Body: []byte("SELECT * FROM T000")})
|
|
412
|
+
+ if err != nil {
|
|
413
|
+
+ t.Fatalf("read-only POST failed: %v", err)
|
|
414
|
+
+ }
|
|
415
|
+
+ want := []string{"HEAD /sap/bc/adt/core/discovery", "GET /sap/bc/adt/core/discovery", "HEAD /sap/bc/adt/discovery", "POST /sap/bc/adt/datapreview/freestyle"}
|
|
416
|
+
+ if strings.Join(requests, "|") != strings.Join(want, "|") {
|
|
417
|
+
+ t.Errorf("requests = %v, want %v", requests, want)
|
|
418
|
+
+ }
|
|
419
|
+
+}
|
|
420
|
+
+
|
|
421
|
+
+func TestCSRFAlternateDiscoveryFailureIncludesStatuses(t *testing.T) {
|
|
422
|
+
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
423
|
+
+ if r.URL.Path == "/sap/bc/adt/core/discovery" {
|
|
424
|
+
+ w.WriteHeader(http.StatusInternalServerError)
|
|
425
|
+
+ } else {
|
|
426
|
+
+ w.WriteHeader(http.StatusForbidden)
|
|
427
|
+
+ }
|
|
428
|
+
+ }))
|
|
429
|
+
+ defer srv.Close()
|
|
430
|
+
+ tr := newCSRFTestTransport(t, srv)
|
|
431
|
+
+ err := tr.fetchCSRFToken(context.Background())
|
|
432
|
+
+ if err == nil || !strings.Contains(err.Error(), "500") || !strings.Contains(err.Error(), "403") {
|
|
433
|
+
+ t.Fatalf("expected both endpoint failures, got %v", err)
|
|
434
|
+
+ }
|
|
435
|
+
+}
|
|
@@ -14,7 +14,8 @@ function usage() {
|
|
|
14
14
|
'',
|
|
15
15
|
' (default) Packs the workspace and installs the tarball globally, then',
|
|
16
16
|
' runs the normal postinstall wizard (binary provisioning,',
|
|
17
|
-
' BAS destination setup, and
|
|
17
|
+
' BAS destination setup, and bundled agent/skill installation',
|
|
18
|
+
' for the selected harnesses).',
|
|
18
19
|
' --link Installs with npm link instead of a tarball; edits to src/',
|
|
19
20
|
' and scripts/ take effect immediately without reinstalling.',
|
|
20
21
|
' --verify Verifies the global sap-ai-dev command resolves to this',
|
|
@@ -1,115 +1,7 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
-
import { mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
1
|
import { homedir } from 'node:os';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { installUserAssets } from '../src/install-user-assets.mjs';
|
|
3
|
+
import { harnessById } from '../src/harnesses.mjs';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const legacyManifestName = '.bas-mcp-addon-assets.json';
|
|
10
|
-
|
|
11
|
-
function digest(content) {
|
|
12
|
-
return createHash('sha256').update(content).digest('hex');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function collectFiles(sourceRoot, targetPrefix, relativePath = '') {
|
|
16
|
-
const files = [];
|
|
17
|
-
const entries = await readdir(join(sourceRoot, relativePath), { withFileTypes: true });
|
|
18
|
-
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
19
|
-
for (const entry of entries) {
|
|
20
|
-
const childPath = join(relativePath, entry.name);
|
|
21
|
-
if (entry.isDirectory()) {
|
|
22
|
-
files.push(...await collectFiles(sourceRoot, targetPrefix, childPath));
|
|
23
|
-
} else if (entry.isFile()) {
|
|
24
|
-
files.push({ source: join(sourceRoot, childPath), target: join(targetPrefix, childPath) });
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return files;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function readManagedFiles(path) {
|
|
31
|
-
try {
|
|
32
|
-
const manifest = JSON.parse(await readFile(path, 'utf8'));
|
|
33
|
-
if (manifest === null || typeof manifest !== 'object' || manifest.version !== 1 || manifest.files === null || typeof manifest.files !== 'object' || Array.isArray(manifest.files)) return {};
|
|
34
|
-
return manifest.files;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
if (error.code === 'ENOENT' || error instanceof SyntaxError) return {};
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function replaceFile(path, content) {
|
|
42
|
-
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
43
|
-
try {
|
|
44
|
-
await writeFile(temporary, content, { flag: 'wx', mode: 0o644 });
|
|
45
|
-
await rename(temporary, path);
|
|
46
|
-
} finally {
|
|
47
|
-
await rm(temporary, { force: true });
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export async function installUserCopilotAssets({ home = process.env.HOME || homedir(), root = packageRoot } = {}) {
|
|
52
|
-
const sourceRoot = join(root, '.github');
|
|
53
|
-
const files = [
|
|
54
|
-
...await collectFiles(join(sourceRoot, 'agents'), 'agents'),
|
|
55
|
-
...await collectFiles(join(sourceRoot, 'skills'), 'skills')
|
|
56
|
-
];
|
|
57
|
-
if (!files.some(file => file.target === join('agents', 'abap-developer.agent.md'))) {
|
|
58
|
-
throw new Error('Packaged ABAP Developer agent profile is missing');
|
|
59
|
-
}
|
|
60
|
-
if (!files.some(file => file.target.startsWith(`skills${sep}`) && file.target.endsWith('SKILL.md'))) {
|
|
61
|
-
throw new Error('Packaged Agent Skills are missing');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const copilotRoot = join(home, '.copilot');
|
|
65
|
-
const manifestPath = join(copilotRoot, manifestName);
|
|
66
|
-
const legacyManifestPath = join(copilotRoot, legacyManifestName);
|
|
67
|
-
await mkdir(copilotRoot, { recursive: true, mode: 0o755 });
|
|
68
|
-
const legacyFiles = await readManagedFiles(legacyManifestPath);
|
|
69
|
-
const currentFiles = await readManagedFiles(manifestPath);
|
|
70
|
-
const previousFiles = { ...legacyFiles, ...currentFiles };
|
|
71
|
-
const managedFiles = { ...previousFiles };
|
|
72
|
-
const conflicts = [];
|
|
73
|
-
let installed = 0;
|
|
74
|
-
let updated = 0;
|
|
75
|
-
let unchanged = 0;
|
|
76
|
-
|
|
77
|
-
for (const file of files) {
|
|
78
|
-
const destination = join(copilotRoot, file.target);
|
|
79
|
-
const content = await readFile(file.source);
|
|
80
|
-
const contentHash = digest(content);
|
|
81
|
-
await mkdir(dirname(destination), { recursive: true, mode: 0o755 });
|
|
82
|
-
|
|
83
|
-
let existing;
|
|
84
|
-
try {
|
|
85
|
-
existing = await readFile(destination);
|
|
86
|
-
} catch (error) {
|
|
87
|
-
if (error.code !== 'ENOENT') throw error;
|
|
88
|
-
try {
|
|
89
|
-
await writeFile(destination, content, { flag: 'wx', mode: 0o644 });
|
|
90
|
-
managedFiles[file.target] = contentHash;
|
|
91
|
-
installed += 1;
|
|
92
|
-
continue;
|
|
93
|
-
} catch (writeError) {
|
|
94
|
-
if (writeError.code !== 'EEXIST') throw writeError;
|
|
95
|
-
existing = await readFile(destination);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const existingHash = digest(existing);
|
|
100
|
-
if (existingHash === contentHash) {
|
|
101
|
-
managedFiles[file.target] = contentHash;
|
|
102
|
-
unchanged += 1;
|
|
103
|
-
} else if (previousFiles[file.target] === existingHash) {
|
|
104
|
-
await replaceFile(destination, content);
|
|
105
|
-
managedFiles[file.target] = contentHash;
|
|
106
|
-
updated += 1;
|
|
107
|
-
} else {
|
|
108
|
-
conflicts.push(file.target);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
await replaceFile(manifestPath, `${JSON.stringify({ version: 1, files: managedFiles }, null, 2)}\n`);
|
|
113
|
-
await rm(legacyManifestPath, { force: true });
|
|
114
|
-
return { root: copilotRoot, installed, updated, unchanged, conflicts };
|
|
5
|
+
export async function installUserCopilotAssets({ home = process.env.HOME || homedir(), root } = {}) {
|
|
6
|
+
return installUserAssets({ harness: harnessById('github-copilot'), home, root });
|
|
115
7
|
}
|