@patimweb/pi-abap-fs 1.0.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.
@@ -0,0 +1,62 @@
1
+ /**
2
+ * abap_read tool — Read the source code of an ABAP object.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { resolveObject, withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+
9
+ export const AbapReadTool = {
10
+ name: "abap_read",
11
+ label: "Read ABAP Source",
12
+ description:
13
+ "Read the main source code of an ABAP object (class, program, function group, interface, …). Provide either a name (with optional type hint) or an object URL. Returns the full source as text.",
14
+ parameters: Type.Object({
15
+ profile: Type.Optional(
16
+ Type.String({
17
+ description: "Profile name to use. Uses active profile if omitted.",
18
+ }),
19
+ ),
20
+ name: Type.Optional(
21
+ Type.String({ description: "Object name, e.g. 'ZCL_MY_CLASS'" }),
22
+ ),
23
+ objectUrl: Type.Optional(
24
+ Type.String({
25
+ description:
26
+ "Object URL, e.g. '/sap/bc/adt/oo/classes/zcl_my_class'. Takes precedence over name.",
27
+ }),
28
+ ),
29
+ type: Type.Optional(
30
+ Type.String({ description: "Optional type hint for search, e.g. 'CLAS/OC'." }),
31
+ ),
32
+ }),
33
+
34
+ async execute(
35
+ _toolCallId: string,
36
+ params: { profile?: string; name?: string; objectUrl?: string; type?: string },
37
+ _signal: AbortSignal,
38
+ ) {
39
+ const config = resolveConfig(params.profile);
40
+ const { source, resolved } = await withClient(config, async (client) => {
41
+ const resolved = await resolveObject(client, params);
42
+ const source = await client.getObjectSource(resolved.mainInclude);
43
+ return { source, resolved };
44
+ });
45
+
46
+ return {
47
+ content: [
48
+ {
49
+ type: "text" as const,
50
+ text: source,
51
+ },
52
+ ],
53
+ details: {
54
+ name: resolved.name,
55
+ type: resolved.type,
56
+ objectUrl: resolved.objectUrl,
57
+ mainInclude: resolved.mainInclude,
58
+ lines: source.split("\n").length,
59
+ },
60
+ };
61
+ },
62
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * abap_search tool — Search ABAP objects by name pattern.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatSearchResults } from "../formatting/formatters.ts";
9
+
10
+ export const AbapSearchTool = {
11
+ name: "abap_search",
12
+ label: "Search ABAP Objects",
13
+ description:
14
+ "Search ABAP objects by name pattern (supports wildcards on most systems). Returns object name, type, package and object URL for each match. Use the returned object URL with abap_read, abap_object_structure, etc.",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ query: Type.String({
22
+ description: "Search pattern, e.g. 'ZCL_MY_CLASS' or 'Z*PRICING*'",
23
+ }),
24
+ objectType: Type.Optional(
25
+ Type.String({
26
+ description:
27
+ "Optional object type filter. The first part is used, e.g. 'CLAS', 'PROG', 'FUGR', 'INTF', 'TABL'. Use abap_object_types to see available types.",
28
+ }),
29
+ ),
30
+ max: Type.Optional(
31
+ Type.Number({ description: "Maximum number of results (default 100)." }),
32
+ ),
33
+ }),
34
+
35
+ async execute(
36
+ _toolCallId: string,
37
+ params: { profile?: string; query: string; objectType?: string; max?: number },
38
+ _signal: AbortSignal,
39
+ ) {
40
+ const config = resolveConfig(params.profile);
41
+ const max = params.max ?? 100;
42
+
43
+ const results = await withClient(config, (client) =>
44
+ client.searchObject(params.query, params.objectType, max),
45
+ );
46
+
47
+ return {
48
+ content: [
49
+ {
50
+ type: "text" as const,
51
+ text: formatSearchResults(results, params.query),
52
+ },
53
+ ],
54
+ details: {
55
+ count: results.length,
56
+ query: params.query,
57
+ results: results.map((r) => ({
58
+ name: r["adtcore:name"],
59
+ type: r["adtcore:type"],
60
+ package: r["adtcore:packageName"],
61
+ description: r["adtcore:description"],
62
+ objectUrl: r["adtcore:uri"],
63
+ })),
64
+ },
65
+ };
66
+ },
67
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * abap_setup tool — Configure an SAP connection profile.
3
+ *
4
+ * Stores a named profile (ADT base URL + credentials). A profile is
5
+ * automatically set as active if it's the first one. Credentials are stored
6
+ * in ~/.pi/abap-config.json, readable only by the owner.
7
+ */
8
+
9
+ import { Type } from "typebox";
10
+ import { saveProfile } from "../config.ts";
11
+ import type { AbapProfile } from "../types.ts";
12
+
13
+ export interface SetupParams {
14
+ name: string;
15
+ url: string;
16
+ username: string;
17
+ password: string;
18
+ client?: string;
19
+ language?: string;
20
+ allowUnauthorized?: boolean;
21
+ }
22
+
23
+ export const AbapSetupTool = {
24
+ name: "abap_setup",
25
+ label: "ABAP Setup",
26
+ description:
27
+ "Configure an SAP system connection profile (ADT protocol). Call this first before using any other ABAP tools. Credentials are stored in ~/.pi/abap-config.json (readable only by you). The SAP system must have ADT (ABAP Development Tools) enabled.",
28
+ parameters: Type.Object({
29
+ name: Type.String({
30
+ description:
31
+ "Profile name, e.g. 'dev', 'prod'. Use a short, memorable name.",
32
+ }),
33
+ url: Type.String({
34
+ description:
35
+ "ADT base URL, e.g. http://vhcalnplci.bti.local:8000 or https://sap.example.com:44300",
36
+ }),
37
+ username: Type.String({
38
+ description: "SAP logon user (e.g. DEVELOPER)",
39
+ }),
40
+ password: Type.String({
41
+ description: "SAP password",
42
+ }),
43
+ client: Type.Optional(
44
+ Type.String({ description: "SAP client / mandant, e.g. '001'" }),
45
+ ),
46
+ language: Type.Optional(
47
+ Type.String({ description: "Logon language key, e.g. 'EN'" }),
48
+ ),
49
+ allowUnauthorized: Type.Optional(
50
+ Type.Boolean({
51
+ description:
52
+ "Accept self-signed TLS certificates. Defaults to false. Set to true only for on-premise dev systems without a valid certificate.",
53
+ }),
54
+ ),
55
+ }),
56
+
57
+ async execute(_toolCallId: string, params: SetupParams, _signal: AbortSignal) {
58
+ const config: AbapProfile = {
59
+ url: params.url.replace(/\/+$/, ""),
60
+ username: params.username,
61
+ password: params.password,
62
+ ...(params.client !== undefined && { client: params.client }),
63
+ ...(params.language !== undefined && { language: params.language }),
64
+ ...(params.allowUnauthorized !== undefined && {
65
+ allowUnauthorized: params.allowUnauthorized,
66
+ }),
67
+ };
68
+
69
+ saveProfile(params.name, config);
70
+
71
+ return {
72
+ content: [
73
+ {
74
+ type: "text" as const,
75
+ text: `SAP profile "${params.name}" saved and set as active. You can now use all ABAP tools (e.g. abap_test_connection to verify).`,
76
+ },
77
+ ],
78
+ details: { profileName: params.name, configSaved: true },
79
+ };
80
+ },
81
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * abap_status tool — Show current ABAP connection configuration status.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { getProfiles, getActiveProfile } from "../config.ts";
7
+ import { formatProfileStatus } from "../formatting/formatters.ts";
8
+
9
+ export const AbapStatusTool = {
10
+ name: "abap_status",
11
+ label: "ABAP Status",
12
+ description:
13
+ "Show current ABAP connection configuration status (which SAP systems are configured and which is active).",
14
+ parameters: Type.Object({}),
15
+
16
+ async execute(_toolCallId: string, _params: {}, _signal: AbortSignal) {
17
+ const allProfiles = getProfiles();
18
+ const active = getActiveProfile();
19
+
20
+ return {
21
+ content: [
22
+ { type: "text" as const, text: formatProfileStatus(allProfiles, active) },
23
+ ],
24
+ details: {
25
+ configured: Object.keys(allProfiles).length > 0,
26
+ profileCount: Object.keys(allProfiles).length,
27
+ activeProfile: active,
28
+ profiles: Object.keys(allProfiles),
29
+ },
30
+ };
31
+ },
32
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * abap_syntax_check tool — Run a syntax check on ABAP source.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { resolveObject, withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatSyntaxCheck } from "../formatting/formatters.ts";
9
+
10
+ export const AbapSyntaxCheckTool = {
11
+ name: "abap_syntax_check",
12
+ label: "ABAP Syntax Check",
13
+ description:
14
+ "Run a syntax check on source code. Provide the object (name or objectUrl) and the source to check (for unsaved content), or omit source to check the object's currently saved main include.",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ name: Type.Optional(
22
+ Type.String({ description: "Object name, e.g. 'ZCL_MY_CLASS'" }),
23
+ ),
24
+ objectUrl: Type.Optional(
25
+ Type.String({
26
+ description:
27
+ "Object URL, e.g. '/sap/bc/adt/oo/classes/zcl_my_class'. Takes precedence over name.",
28
+ }),
29
+ ),
30
+ type: Type.Optional(
31
+ Type.String({ description: "Optional type hint for search, e.g. 'CLAS/OC'." }),
32
+ ),
33
+ source: Type.Optional(
34
+ Type.String({
35
+ description:
36
+ "Source to check. When omitted, the currently saved main include is checked.",
37
+ }),
38
+ ),
39
+ }),
40
+
41
+ async execute(
42
+ _toolCallId: string,
43
+ params: {
44
+ profile?: string;
45
+ name?: string;
46
+ objectUrl?: string;
47
+ type?: string;
48
+ source?: string;
49
+ },
50
+ _signal: AbortSignal,
51
+ ) {
52
+ const config = resolveConfig(params.profile);
53
+ const { resolved, results } = await withClient(config, async (client) => {
54
+ const resolved = await resolveObject(client, params);
55
+ const source =
56
+ params.source ?? (await client.getObjectSource(resolved.mainInclude));
57
+ const results = await client.syntaxCheck(
58
+ resolved.mainInclude,
59
+ resolved.mainInclude,
60
+ source,
61
+ );
62
+ return { resolved, results };
63
+ });
64
+
65
+ return {
66
+ content: [
67
+ { type: "text" as const, text: formatSyntaxCheck(results) },
68
+ ],
69
+ details: {
70
+ name: resolved.name,
71
+ messageCount: results.length,
72
+ messages: results,
73
+ },
74
+ };
75
+ },
76
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * abap_table tool — Preview the contents of a DDIC table / view.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatQueryResult } from "../formatting/formatters.ts";
9
+
10
+ export const AbapTableTool = {
11
+ name: "abap_table",
12
+ label: "Preview ABAP Table",
13
+ description:
14
+ "Preview the contents of a DDIC table or view. Returns the first N rows. Optionally provide a WHERE-style filter via the sqlQuery parameter (empty string returns all rows up to rowNumber).",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ table: Type.String({
22
+ description: "DDIC table or view name, e.g. 'SFLIGHT' or 'MARA'.",
23
+ }),
24
+ rowNumber: Type.Optional(
25
+ Type.Number({ description: "Maximum rows to return (default 100)." }),
26
+ ),
27
+ filter: Type.Optional(
28
+ Type.String({
29
+ description:
30
+ "Optional SQL WHERE condition (without the WHERE keyword), e.g. \"CARRID = 'LH'\".",
31
+ }),
32
+ ),
33
+ }),
34
+
35
+ async execute(
36
+ _toolCallId: string,
37
+ params: {
38
+ profile?: string;
39
+ table: string;
40
+ rowNumber?: number;
41
+ filter?: string;
42
+ },
43
+ _signal: AbortSignal,
44
+ ) {
45
+ const config = resolveConfig(params.profile);
46
+ const filterSql = params.filter ? `WHERE ${params.filter}` : "";
47
+ const result = await withClient(config, (client) =>
48
+ client.tableContents(
49
+ params.table,
50
+ params.rowNumber ?? 100,
51
+ true,
52
+ filterSql,
53
+ ),
54
+ );
55
+
56
+ return {
57
+ content: [
58
+ { type: "text" as const, text: formatQueryResult(result) },
59
+ ],
60
+ details: {
61
+ table: params.table.toUpperCase(),
62
+ rowCount: result.values.length,
63
+ columns: result.columns.map((c) => c.name),
64
+ },
65
+ };
66
+ },
67
+ };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * abap_test_connection tool — Verify that a profile can log on to the SAP system.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+
9
+ export const AbapTestConnectionTool = {
10
+ name: "abap_test_connection",
11
+ label: "Test ABAP Connection",
12
+ description:
13
+ "Test the connection to an SAP system by logging in and reading the ADT compatibility graph. Use this to verify a profile works after abap_setup.",
14
+ parameters: Type.Object({
15
+ profile: Type.Optional(
16
+ Type.String({
17
+ description: "Profile name to use. Uses active profile if omitted.",
18
+ }),
19
+ ),
20
+ }),
21
+
22
+ async execute(
23
+ _toolCallId: string,
24
+ params: { profile?: string },
25
+ _signal: AbortSignal,
26
+ ) {
27
+ const config = resolveConfig(params.profile);
28
+
29
+ const graph = await withClient(config, async (client) => {
30
+ const compat = await client.adtCompatibiliyGraph();
31
+ const discovery = await client.adtCoreDiscovery();
32
+ return {
33
+ compatibilityNodes: compat.nodes.map((n) => `${n.nameSpace}:${n.name}`),
34
+ discoveryTitles: discovery.map((d) => d.title),
35
+ };
36
+ });
37
+
38
+ return {
39
+ content: [
40
+ {
41
+ type: "text" as const,
42
+ text: `Connected to ${config.url} as ${config.username}. Login successful.`,
43
+ },
44
+ ],
45
+ details: {
46
+ url: config.url,
47
+ username: config.username,
48
+ client: config.client ?? null,
49
+ language: config.language ?? null,
50
+ ...graph,
51
+ },
52
+ };
53
+ },
54
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * abap_transports tool — List the transport requests of the current user.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatTransports } from "../formatting/formatters.ts";
9
+
10
+ export const AbapTransportsTool = {
11
+ name: "abap_transports",
12
+ label: "List ABAP Transports",
13
+ description:
14
+ "List the transport requests of a user. By default lists the transports of the profile's username. Use the returned transport numbers with abap_write (transport parameter).",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ user: Type.Optional(
22
+ Type.String({
23
+ description:
24
+ "User to list transports for. Defaults to the connected user.",
25
+ }),
26
+ ),
27
+ }),
28
+
29
+ async execute(
30
+ _toolCallId: string,
31
+ params: { profile?: string; user?: string },
32
+ _signal: AbortSignal,
33
+ ) {
34
+ const config = resolveConfig(params.profile);
35
+ const transports = await withClient(config, (client) =>
36
+ client.userTransports(params.user ?? config.username),
37
+ );
38
+
39
+ return {
40
+ content: [
41
+ { type: "text" as const, text: formatTransports(transports) },
42
+ ],
43
+ details: { transports },
44
+ };
45
+ },
46
+ };
@@ -0,0 +1,85 @@
1
+ /**
2
+ * abap_unit_test tool — Run ABAP unit tests for a class.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { resolveObject, withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+ import { formatUnitTests } from "../formatting/formatters.ts";
9
+
10
+ export const AbapUnitTestTool = {
11
+ name: "abap_unit_test",
12
+ label: "Run ABAP Unit Tests",
13
+ description:
14
+ "Run ABAP unit tests for a class (or program). Provide the object name or URL. By default only short, harmless tests run; enable dangerous/critical or medium/long durations as needed.",
15
+ parameters: Type.Object({
16
+ profile: Type.Optional(
17
+ Type.String({
18
+ description: "Profile name to use. Uses active profile if omitted.",
19
+ }),
20
+ ),
21
+ name: Type.Optional(
22
+ Type.String({ description: "Class name, e.g. 'ZCL_MY_CLASS'" }),
23
+ ),
24
+ objectUrl: Type.Optional(
25
+ Type.String({
26
+ description:
27
+ "Object URL, e.g. '/sap/bc/adt/oo/classes/zcl_my_class'. Takes precedence over name.",
28
+ }),
29
+ ),
30
+ type: Type.Optional(
31
+ Type.String({ description: "Optional type hint for search, e.g. 'CLAS/OC'." }),
32
+ ),
33
+ dangerous: Type.Optional(
34
+ Type.Boolean({ description: "Include dangerous tests (default false)." }),
35
+ ),
36
+ critical: Type.Optional(
37
+ Type.Boolean({ description: "Include critical tests (default false)." }),
38
+ ),
39
+ medium: Type.Optional(
40
+ Type.Boolean({ description: "Include medium-duration tests (default false)." }),
41
+ ),
42
+ long: Type.Optional(
43
+ Type.Boolean({ description: "Include long-duration tests (default false)." }),
44
+ ),
45
+ }),
46
+
47
+ async execute(
48
+ _toolCallId: string,
49
+ params: {
50
+ profile?: string;
51
+ name?: string;
52
+ objectUrl?: string;
53
+ type?: string;
54
+ dangerous?: boolean;
55
+ critical?: boolean;
56
+ medium?: boolean;
57
+ long?: boolean;
58
+ },
59
+ _signal: AbortSignal,
60
+ ) {
61
+ const config = resolveConfig(params.profile);
62
+ const { resolved, classes } = await withClient(config, async (client) => {
63
+ const resolved = await resolveObject(client, params);
64
+ const classes = await client.unitTestRun(resolved.objectUrl, {
65
+ harmless: true,
66
+ dangerous: params.dangerous ?? false,
67
+ critical: params.critical ?? false,
68
+ short: true,
69
+ medium: params.medium ?? false,
70
+ long: params.long ?? false,
71
+ });
72
+ return { resolved, classes };
73
+ });
74
+
75
+ return {
76
+ content: [
77
+ { type: "text" as const, text: formatUnitTests(classes) },
78
+ ],
79
+ details: {
80
+ name: resolved.name,
81
+ testClasses: classes,
82
+ },
83
+ };
84
+ },
85
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * abap_unlock tool — Release a lock previously obtained with abap_lock.
3
+ */
4
+
5
+ import { Type } from "typebox";
6
+ import { resolveObject, withClient } from "../client.ts";
7
+ import { resolveConfig } from "../config.ts";
8
+
9
+ export const AbapUnlockTool = {
10
+ name: "abap_unlock",
11
+ label: "Unlock ABAP Object",
12
+ description:
13
+ "Release a lock on an ABAP object. Provide the object URL (or name) and the lock handle returned by abap_lock.",
14
+ parameters: Type.Object({
15
+ profile: Type.Optional(
16
+ Type.String({
17
+ description: "Profile name to use. Uses active profile if omitted.",
18
+ }),
19
+ ),
20
+ objectUrl: Type.Optional(
21
+ Type.String({
22
+ description: "Object URL, e.g. '/sap/bc/adt/oo/classes/zcl_my_class'.",
23
+ }),
24
+ ),
25
+ name: Type.Optional(
26
+ Type.String({ description: "Object name (used only if objectUrl omitted)." }),
27
+ ),
28
+ type: Type.Optional(
29
+ Type.String({ description: "Optional type hint for search, e.g. 'CLAS/OC'." }),
30
+ ),
31
+ lockHandle: Type.String({
32
+ description: "The lock handle returned by abap_lock.",
33
+ }),
34
+ }),
35
+
36
+ async execute(
37
+ _toolCallId: string,
38
+ params: {
39
+ profile?: string;
40
+ objectUrl?: string;
41
+ name?: string;
42
+ type?: string;
43
+ lockHandle: string;
44
+ },
45
+ _signal: AbortSignal,
46
+ ) {
47
+ const config = resolveConfig(params.profile);
48
+ const resolved = await withClient(
49
+ config,
50
+ async (client) => {
51
+ const resolved = await resolveObject(client, params);
52
+ await client.unLock(resolved.objectUrl, params.lockHandle);
53
+ return resolved;
54
+ },
55
+ { stateful: true },
56
+ );
57
+
58
+ return {
59
+ content: [
60
+ {
61
+ type: "text" as const,
62
+ text: `Unlocked ${resolved.name} (${resolved.objectUrl}).`,
63
+ },
64
+ ],
65
+ details: {
66
+ name: resolved.name,
67
+ objectUrl: resolved.objectUrl,
68
+ },
69
+ };
70
+ },
71
+ };