mcp-for-i 0.1.13 → 0.3.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 +27 -0
- package/dist/config/store.js +229 -109
- package/dist/controlplane/auth.js +128 -0
- package/dist/controlplane/clientIntegrationService.js +83 -4
- package/dist/controlplane/commands.js +52 -11
- package/dist/controlplane/connectionService.js +110 -5
- package/dist/controlplane/connectionTest.js +329 -0
- package/dist/controlplane/errors.js +12 -0
- package/dist/controlplane/runtimeService.js +98 -26
- package/dist/controlplane/server.js +178 -11
- package/dist/controlplane/serverShared.js +25 -6
- package/dist/controlplane/serviceManager.js +147 -100
- package/dist/controlplane/ui.js +1783 -920
- package/dist/ibmi/CompileTools.js +29 -1
- package/dist/ibmi/IBMiContent.js +435 -63
- package/dist/ibmi/IbmDocs.js +685 -0
- package/dist/ibmi/LocalLanguageActions.js +129 -3
- package/dist/ibmi/Search.js +158 -52
- package/dist/ibmi/Tools.js +7 -10
- package/dist/ibmi/client.js +220 -31
- package/dist/ibmi/evfevent.js +256 -0
- package/dist/ibmi/knownHosts.js +190 -0
- package/dist/ibmi/libraryList.js +93 -0
- package/dist/ibmi/sourceInspect.js +815 -0
- package/dist/ibmi/sqlJob.js +888 -0
- package/dist/ibmi/sqlParse.js +317 -0
- package/dist/ibmi/tn5250/certTrust.js +59 -0
- package/dist/ibmi/tn5250/client.js +334 -0
- package/dist/ibmi/tn5250/datastream.js +109 -0
- package/dist/ibmi/tn5250/ebcdic.js +93 -0
- package/dist/ibmi/tn5250/ebcdicTables.js +27 -0
- package/dist/ibmi/tn5250/index.js +6 -0
- package/dist/ibmi/tn5250/screen.js +1033 -0
- package/dist/ibmi/tn5250/telnet.js +186 -0
- package/dist/ibmi/variables.js +18 -7
- package/dist/index.js +79 -24
- package/dist/mcp/annotations.js +155 -0
- package/dist/mcp/audit.js +490 -133
- package/dist/mcp/confirm.js +48 -0
- package/dist/mcp/context.js +43 -5
- package/dist/mcp/deployMethods.js +192 -0
- package/dist/mcp/logger.js +5 -10
- package/dist/mcp/modules/bulkMembers.js +476 -0
- package/dist/mcp/modules/changedLines.js +189 -0
- package/dist/mcp/modules/compare.js +198 -0
- package/dist/mcp/modules/compile.js +515 -0
- package/dist/mcp/modules/ifsOps.js +123 -0
- package/dist/mcp/modules/index.js +38 -0
- package/dist/mcp/modules/inspect.js +811 -0
- package/dist/mcp/modules/libl.js +246 -0
- package/dist/mcp/modules/members.js +171 -0
- package/dist/mcp/modules/objects.js +339 -0
- package/dist/mcp/modules/system.js +266 -0
- package/dist/mcp/modules/tn5250.js +800 -0
- package/dist/mcp/modules/tn5250App.js +194 -0
- package/dist/mcp/modules/tn5250Font.js +8 -0
- package/dist/mcp/modules/tn5250Live.js +221 -0
- package/dist/mcp/modules/tn5250LivePage.js +480 -0
- package/dist/mcp/policy.js +412 -0
- package/dist/mcp/tools.js +1719 -698
- package/dist/security/credentialStore.js +62 -13
- package/dist/security/localPaths.js +138 -0
- package/dist/security/redact.js +79 -0
- package/dist/util/atomicWrite.js +101 -0
- package/dist/util/fileLock.js +206 -0
- package/docs/capabilities.md +264 -220
- package/docs/configuration.md +193 -60
- package/docs/examples.md +109 -73
- package/docs/overview.md +15 -15
- package/docs/security.md +101 -16
- package/docs/tools.md +367 -188
- package/package.json +5 -2
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in actions. Streamfile actions compile an IFS source file
|
|
3
|
+
* (&RELATIVEPATH / &FULLPATH); member actions compile a source member
|
|
4
|
+
* (&OPENLIB / &OPENSPF / &OPENMBR). Running an action against the other kind
|
|
5
|
+
* of target is refused.
|
|
6
|
+
*/
|
|
1
7
|
export const LocalLanguageActions = {
|
|
2
8
|
RPGLE: [
|
|
3
9
|
{
|
|
10
|
+
type: `streamfile`,
|
|
4
11
|
name: `Create RPGLE Program`,
|
|
5
12
|
command: `CRTBNDRPG PGM(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF) DBGVIEW(*SOURCE) TGTCCSID(*JOB)`,
|
|
6
13
|
deployFirst: true,
|
|
@@ -8,6 +15,7 @@ export const LocalLanguageActions = {
|
|
|
8
15
|
extensions: [`RPGLE`]
|
|
9
16
|
},
|
|
10
17
|
{
|
|
18
|
+
type: `streamfile`,
|
|
11
19
|
name: `Create RPGLE Module`,
|
|
12
20
|
command: `CRTRPGMOD MODULE(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF) DBGVIEW(*SOURCE) TGTCCSID(*JOB)`,
|
|
13
21
|
deployFirst: true,
|
|
@@ -15,6 +23,7 @@ export const LocalLanguageActions = {
|
|
|
15
23
|
extensions: [`RPGLE`]
|
|
16
24
|
},
|
|
17
25
|
{
|
|
26
|
+
type: `streamfile`,
|
|
18
27
|
name: `Create SQLRPGLE Program`,
|
|
19
28
|
command: `CRTSQLRPGI OBJ(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF) DBGVIEW(*SOURCE) CLOSQLCSR(*ENDMOD) CVTCCSID(*JOB) COMPILEOPT('TGTCCSID(*JOB)') RPGPPOPT(*LVL2)`,
|
|
20
29
|
deployFirst: true,
|
|
@@ -22,6 +31,7 @@ export const LocalLanguageActions = {
|
|
|
22
31
|
extensions: [`SQLRPGLE`]
|
|
23
32
|
},
|
|
24
33
|
{
|
|
34
|
+
type: `streamfile`,
|
|
25
35
|
name: `Create SQLRPGLE Module`,
|
|
26
36
|
command: `CRTSQLRPGI OBJ(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OBJTYPE(*MODULE) OPTION(*EVENTF) DBGVIEW(*SOURCE) CLOSQLCSR(*ENDMOD) CVTCCSID(*JOB) COMPILEOPT('TGTCCSID(*JOB)') RPGPPOPT(*LVL2)`,
|
|
27
37
|
deployFirst: true,
|
|
@@ -31,6 +41,7 @@ export const LocalLanguageActions = {
|
|
|
31
41
|
],
|
|
32
42
|
COBOL: [
|
|
33
43
|
{
|
|
44
|
+
type: `streamfile`,
|
|
34
45
|
name: `Create COBOL Program (SQL)`,
|
|
35
46
|
command: `CRTSQLCBLI OBJ(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF) DBGVIEW(*SOURCE) CLOSQLCSR(*ENDMOD) CVTCCSID(*JOB) TOSRCFILE(&CURLIB/QSQLTEMP)`,
|
|
36
47
|
deployFirst: true,
|
|
@@ -38,6 +49,7 @@ export const LocalLanguageActions = {
|
|
|
38
49
|
extensions: [`SQLCBL`, `SQLCBBLE`, `SQLCBLLE`, `COB`, `CBLLE`, `CBL`, `CBBLE`]
|
|
39
50
|
},
|
|
40
51
|
{
|
|
52
|
+
type: `streamfile`,
|
|
41
53
|
name: `Create COBOL Module (SQL)`,
|
|
42
54
|
command: `CRTSQLCBLI OBJ(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OBJTYPE(*MODULE) OPTION(*EVENTF) DBGVIEW(*SOURCE) CLOSQLCSR(*ENDMOD) CVTCCSID(*JOB) TOSRCFILE(&CURLIB/QSQLTEMP)`,
|
|
43
55
|
deployFirst: true,
|
|
@@ -47,6 +59,7 @@ export const LocalLanguageActions = {
|
|
|
47
59
|
],
|
|
48
60
|
C: [
|
|
49
61
|
{
|
|
62
|
+
type: `streamfile`,
|
|
50
63
|
name: `Create C Program`,
|
|
51
64
|
command: `CRTBNDC PGM(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF) DBGVIEW(*SOURCE) TGTCCSID(*JOB)`,
|
|
52
65
|
deployFirst: true,
|
|
@@ -54,6 +67,7 @@ export const LocalLanguageActions = {
|
|
|
54
67
|
extensions: [`C`]
|
|
55
68
|
},
|
|
56
69
|
{
|
|
70
|
+
type: `streamfile`,
|
|
57
71
|
name: `Create C Module`,
|
|
58
72
|
command: `CRTCMOD MODULE(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF) DBGVIEW(*SOURCE) TGTCCSID(*JOB)`,
|
|
59
73
|
deployFirst: true,
|
|
@@ -63,6 +77,7 @@ export const LocalLanguageActions = {
|
|
|
63
77
|
],
|
|
64
78
|
CPP: [
|
|
65
79
|
{
|
|
80
|
+
type: `streamfile`,
|
|
66
81
|
name: `Create CPP Program`,
|
|
67
82
|
command: `CRTBNDCPP PGM(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF) DBGVIEW(*SOURCE) TGTCCSID(*JOB)`,
|
|
68
83
|
deployFirst: true,
|
|
@@ -70,6 +85,7 @@ export const LocalLanguageActions = {
|
|
|
70
85
|
extensions: [`CPP`]
|
|
71
86
|
},
|
|
72
87
|
{
|
|
88
|
+
type: `streamfile`,
|
|
73
89
|
name: `Create CPP Module`,
|
|
74
90
|
command: `CRTCPPMOD MODULE(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF) DBGVIEW(*SOURCE) TGTCCSID(*JOB)`,
|
|
75
91
|
deployFirst: true,
|
|
@@ -79,6 +95,7 @@ export const LocalLanguageActions = {
|
|
|
79
95
|
],
|
|
80
96
|
CLLE: [
|
|
81
97
|
{
|
|
98
|
+
type: `streamfile`,
|
|
82
99
|
extensions: [`CLP`, `CLLE`],
|
|
83
100
|
name: `Create Bound CL Program`,
|
|
84
101
|
command: `CRTBNDCL PGM(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF) DBGVIEW(*SOURCE)`,
|
|
@@ -88,6 +105,7 @@ export const LocalLanguageActions = {
|
|
|
88
105
|
],
|
|
89
106
|
CMD: [
|
|
90
107
|
{
|
|
108
|
+
type: `streamfile`,
|
|
91
109
|
extensions: [`cmd`],
|
|
92
110
|
name: `Create Command`,
|
|
93
111
|
command: `CRTCMD CMD(&CURLIB/&NAME) PGM(&CURLIB/&NAME) SRCSTMF('&RELATIVEPATH') OPTION(*EVENTF)`,
|
|
@@ -97,6 +115,7 @@ export const LocalLanguageActions = {
|
|
|
97
115
|
],
|
|
98
116
|
SQL: [
|
|
99
117
|
{
|
|
118
|
+
type: `streamfile`,
|
|
100
119
|
extensions: [`SQL`, `TABLE`, `VIEW`, `SQLPRC`, `SQLUDF`, `SQLUDT`, `SQLTRG`, `SQLALIAS`, `SQLSEQ`],
|
|
101
120
|
name: `Run SQL Statements (RUNSQLSTM)`,
|
|
102
121
|
command: `RUNSQLSTM SRCSTMF('&FULLPATH') COMMIT(*NONE) NAMING(*SQL)`,
|
|
@@ -106,11 +125,11 @@ export const LocalLanguageActions = {
|
|
|
106
125
|
],
|
|
107
126
|
DSPF: [
|
|
108
127
|
{
|
|
128
|
+
type: "member",
|
|
109
129
|
name: "Create DSPF",
|
|
110
|
-
command: "CRTDSPF FILE(&
|
|
130
|
+
command: "CRTDSPF FILE(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) RSTDSP(*NO) OPTION(*EVENTF)",
|
|
111
131
|
environment: "ile",
|
|
112
|
-
|
|
113
|
-
extensions: ["dspf"]
|
|
132
|
+
extensions: ["DSPF"]
|
|
114
133
|
}
|
|
115
134
|
],
|
|
116
135
|
"Service Programs": [
|
|
@@ -121,6 +140,7 @@ export const LocalLanguageActions = {
|
|
|
121
140
|
environment: `ile`
|
|
122
141
|
},
|
|
123
142
|
{
|
|
143
|
+
type: `streamfile`,
|
|
124
144
|
extensions: ["BND", "BINDER"],
|
|
125
145
|
deployFirst: true,
|
|
126
146
|
name: "Create Service Program (CRTSRVPGM with source)",
|
|
@@ -130,6 +150,7 @@ export const LocalLanguageActions = {
|
|
|
130
150
|
],
|
|
131
151
|
"GNU Make": [
|
|
132
152
|
{
|
|
153
|
+
type: `streamfile`,
|
|
133
154
|
extensions: [`GLOBAL`],
|
|
134
155
|
name: `Build all with gmake`,
|
|
135
156
|
command: `/QOpenSys/pkgs/bin/gmake BUILDLIB=&CURLIB ERR=*EVENTF`,
|
|
@@ -137,6 +158,7 @@ export const LocalLanguageActions = {
|
|
|
137
158
|
deployFirst: true
|
|
138
159
|
},
|
|
139
160
|
{
|
|
161
|
+
type: `streamfile`,
|
|
140
162
|
extensions: [`GLOBAL`],
|
|
141
163
|
name: `Build current with gmake`,
|
|
142
164
|
command: `/QOpenSys/pkgs/bin/gmake &BASENAME BUILDLIB=&CURLIB ERR=*EVENTF`,
|
|
@@ -146,6 +168,7 @@ export const LocalLanguageActions = {
|
|
|
146
168
|
],
|
|
147
169
|
"The Object Builder for i (TOBi)": [
|
|
148
170
|
{
|
|
171
|
+
type: `streamfile`,
|
|
149
172
|
extensions: [`GLOBAL`],
|
|
150
173
|
name: `Build all with TOBi`,
|
|
151
174
|
command: `OPT=*EVENTF BUILDLIB=&CURLIB /QOpenSys/pkgs/bin/makei build`,
|
|
@@ -154,6 +177,7 @@ export const LocalLanguageActions = {
|
|
|
154
177
|
postDownload: [".logs", ".evfevent"]
|
|
155
178
|
},
|
|
156
179
|
{
|
|
180
|
+
type: `streamfile`,
|
|
157
181
|
extensions: [`GLOBAL`],
|
|
158
182
|
name: `Build current with TOBi`,
|
|
159
183
|
command: `OPT=*EVENTF BUILDLIB=&CURLIB /QOpenSys/pkgs/bin/makei compile -f &BASENAME`,
|
|
@@ -164,6 +188,7 @@ export const LocalLanguageActions = {
|
|
|
164
188
|
],
|
|
165
189
|
"Source Orbit": [
|
|
166
190
|
{
|
|
191
|
+
type: `streamfile`,
|
|
167
192
|
name: "Build current with Source Orbit",
|
|
168
193
|
command: "so -bf make -s &RELATIVEPATH && /QOpenSys/pkgs/bin/gmake LIBL='&LIBLS' BIN_LIB=&CURLIB OPT=*EVENTF",
|
|
169
194
|
environment: "pase",
|
|
@@ -172,6 +197,7 @@ export const LocalLanguageActions = {
|
|
|
172
197
|
postDownload: [".evfevent/"]
|
|
173
198
|
},
|
|
174
199
|
{
|
|
200
|
+
type: `streamfile`,
|
|
175
201
|
name: "Build entire project with Source Orbit",
|
|
176
202
|
command: "so -bf make && /QOpenSys/pkgs/bin/gmake LIBL='&LIBLS' BIN_LIB=&CURLIB OPT=*EVENTF",
|
|
177
203
|
environment: "pase",
|
|
@@ -179,5 +205,105 @@ export const LocalLanguageActions = {
|
|
|
179
205
|
extensions: ["GLOBAL"],
|
|
180
206
|
postDownload: [".evfevent/"]
|
|
181
207
|
}
|
|
208
|
+
],
|
|
209
|
+
"Source Members": [
|
|
210
|
+
{
|
|
211
|
+
type: `member`,
|
|
212
|
+
name: `Create Bound RPG Program (CRTBNDRPG)`,
|
|
213
|
+
command: `CRTBNDRPG PGM(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF) DBGVIEW(*SOURCE)`,
|
|
214
|
+
environment: `ile`,
|
|
215
|
+
extensions: [`RPGLE`, `RPG`]
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
type: `member`,
|
|
219
|
+
name: `Create RPG Module (CRTRPGMOD)`,
|
|
220
|
+
command: `CRTRPGMOD MODULE(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF) DBGVIEW(*SOURCE)`,
|
|
221
|
+
environment: `ile`,
|
|
222
|
+
extensions: [`RPGLE`, `RPG`]
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
type: `member`,
|
|
226
|
+
name: `Create SQL ILE RPG Program (CRTSQLRPGI)`,
|
|
227
|
+
command: `CRTSQLRPGI OBJ(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) CLOSQLCSR(*ENDMOD) OPTION(*EVENTF) DBGVIEW(*SOURCE) CVTCCSID(*JOB)`,
|
|
228
|
+
environment: `ile`,
|
|
229
|
+
extensions: [`SQLRPGLE`]
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
type: `member`,
|
|
233
|
+
name: `Create SQL ILE RPG Module (CRTSQLRPGI)`,
|
|
234
|
+
command: `CRTSQLRPGI OBJ(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OBJTYPE(*MODULE) CLOSQLCSR(*ENDMOD) OPTION(*EVENTF) DBGVIEW(*SOURCE) CVTCCSID(*JOB)`,
|
|
235
|
+
environment: `ile`,
|
|
236
|
+
extensions: [`SQLRPGLE`]
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
type: `member`,
|
|
240
|
+
name: `Create Bound COBOL Program (CRTBNDCBL)`,
|
|
241
|
+
command: `CRTBNDCBL PGM(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF) DBGVIEW(*SOURCE)`,
|
|
242
|
+
environment: `ile`,
|
|
243
|
+
extensions: [`CBLLE`]
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
type: `member`,
|
|
247
|
+
name: `Create SQL ILE COBOL Program (CRTSQLCBLI)`,
|
|
248
|
+
command: `CRTSQLCBLI OBJ(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) CLOSQLCSR(*ENDMOD) OPTION(*EVENTF) DBGVIEW(*SOURCE)`,
|
|
249
|
+
environment: `ile`,
|
|
250
|
+
extensions: [`SQLCBLLE`]
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
type: `member`,
|
|
254
|
+
name: `Create Bound C Program (CRTBNDC)`,
|
|
255
|
+
command: `CRTBNDC PGM(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF) DBGVIEW(*SOURCE)`,
|
|
256
|
+
environment: `ile`,
|
|
257
|
+
extensions: [`C`]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
type: `member`,
|
|
261
|
+
name: `Create Bound CL Program (CRTBNDCL)`,
|
|
262
|
+
command: `CRTBNDCL PGM(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF) DBGVIEW(*SOURCE)`,
|
|
263
|
+
environment: `ile`,
|
|
264
|
+
extensions: [`CLLE`]
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
type: `member`,
|
|
268
|
+
name: `Create CL Module (CRTCLMOD)`,
|
|
269
|
+
command: `CRTCLMOD MODULE(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF) DBGVIEW(*SOURCE)`,
|
|
270
|
+
environment: `ile`,
|
|
271
|
+
extensions: [`CLLE`]
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
type: `member`,
|
|
275
|
+
name: `Create Command (CRTCMD)`,
|
|
276
|
+
command: `CRTCMD CMD(&OPENLIB/&OPENMBR) PGM(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF)`,
|
|
277
|
+
environment: `ile`,
|
|
278
|
+
extensions: [`CMD`]
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
type: `member`,
|
|
282
|
+
name: `Create Physical File (CRTPF)`,
|
|
283
|
+
command: `CRTPF FILE(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF)`,
|
|
284
|
+
environment: `ile`,
|
|
285
|
+
extensions: [`PF`]
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
type: `member`,
|
|
289
|
+
name: `Create Logical File (CRTLF)`,
|
|
290
|
+
command: `CRTLF FILE(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF)`,
|
|
291
|
+
environment: `ile`,
|
|
292
|
+
extensions: [`LF`]
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
type: `member`,
|
|
296
|
+
name: `Create Printer File (CRTPRTF)`,
|
|
297
|
+
command: `CRTPRTF FILE(&OPENLIB/&OPENMBR) SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) OPTION(*EVENTF)`,
|
|
298
|
+
environment: `ile`,
|
|
299
|
+
extensions: [`PRTF`]
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
type: `member`,
|
|
303
|
+
name: `Run SQL Statements from member (RUNSQLSTM)`,
|
|
304
|
+
command: `RUNSQLSTM SRCFILE(&OPENLIB/&OPENSPF) SRCMBR(&OPENMBR) COMMIT(*NONE) NAMING(*SQL)`,
|
|
305
|
+
environment: `ile`,
|
|
306
|
+
extensions: [`SQL`]
|
|
307
|
+
}
|
|
182
308
|
]
|
|
183
309
|
};
|
package/dist/ibmi/Search.js
CHANGED
|
@@ -14,6 +14,12 @@ export var Search;
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
async function searchMembers(connection, library, sourceFile, searchTerm, members, readOnly) {
|
|
17
|
+
return searchMembersTerms(connection, library, sourceFile, [searchTerm], members, readOnly);
|
|
18
|
+
}
|
|
19
|
+
Search.searchMembers = searchMembers;
|
|
20
|
+
Search.MEMBER_SEARCH_DEFAULT_MAX_MATCHES = 100;
|
|
21
|
+
Search.MEMBER_SEARCH_MAX_MATCHES = 1_000;
|
|
22
|
+
async function searchMembersTerms(connection, library, sourceFile, searchTerms, members, readOnly, options) {
|
|
17
23
|
const lib = String(library || "").trim().toUpperCase();
|
|
18
24
|
const src = String(sourceFile || "").trim().toUpperCase();
|
|
19
25
|
if (!connection.validQsysName(lib)) {
|
|
@@ -22,6 +28,11 @@ export var Search;
|
|
|
22
28
|
if (!connection.validQsysName(src)) {
|
|
23
29
|
throw new Error(`Invalid source file: ${sourceFile}`);
|
|
24
30
|
}
|
|
31
|
+
const terms = searchTerms.map(term => String(term || "")).filter(term => term.length > 0);
|
|
32
|
+
if (terms.length < 1 || terms.length > 20)
|
|
33
|
+
throw new Error("Provide between 1 and 20 search terms");
|
|
34
|
+
if (terms.some(term => term.includes("\n") || term.includes("\r")))
|
|
35
|
+
throw new Error("Search terms may not contain newlines");
|
|
25
36
|
let detailedMembers;
|
|
26
37
|
let memberFilter;
|
|
27
38
|
const pfgrep = connection.remoteFeatures.pfgrep;
|
|
@@ -29,42 +40,127 @@ export var Search;
|
|
|
29
40
|
if (!isSafeMemberToken(members)) {
|
|
30
41
|
throw new Error(`Invalid members filter: ${members}`);
|
|
31
42
|
}
|
|
32
|
-
memberFilter =
|
|
43
|
+
memberFilter = memberGlobToken(members);
|
|
33
44
|
}
|
|
34
45
|
else if (Array.isArray(members)) {
|
|
46
|
+
const normalizedMembers = members.map(member => typeof member === "string"
|
|
47
|
+
? { library: lib, file: src, name: member, extension: "" }
|
|
48
|
+
: member);
|
|
35
49
|
if (members.length > 1000) {
|
|
36
|
-
detailedMembers =
|
|
50
|
+
detailedMembers = normalizedMembers;
|
|
37
51
|
memberFilter = "*.MBR";
|
|
38
52
|
}
|
|
39
53
|
else {
|
|
40
|
-
|
|
54
|
+
normalizedMembers.forEach(member => {
|
|
41
55
|
if (!isSafeMemberToken(member.name)) {
|
|
42
56
|
throw new Error(`Invalid member name in filter: ${member.name}`);
|
|
43
57
|
}
|
|
44
58
|
});
|
|
45
|
-
memberFilter =
|
|
59
|
+
memberFilter = normalizedMembers.map(member => memberGlobToken(member.name)).join(` `);
|
|
46
60
|
}
|
|
47
61
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
// pfgrep only supports after-context (-A); before-context needs grep.
|
|
63
|
+
const requestedBefore = clampCount(options?.before, 0, 50, 0);
|
|
64
|
+
const before = pfgrep ? 0 : requestedBefore;
|
|
65
|
+
const after = clampCount(options?.after, 0, 50, 0);
|
|
66
|
+
const maxMatches = clampCount(options?.maxMatches, 1, Search.MEMBER_SEARCH_MAX_MATCHES, Search.MEMBER_SEARCH_DEFAULT_MAX_MATCHES);
|
|
67
|
+
const contextFlags = [
|
|
68
|
+
before ? `-B ${before}` : "",
|
|
69
|
+
after ? `-A ${after}` : ""
|
|
70
|
+
].filter(Boolean).join(" ");
|
|
71
|
+
// pfgrep documents -F (literal), -f - (patterns from stdin, one per line),
|
|
72
|
+
// -m (matches per member), -A (after-context; it has no -B), -n, -H, -i and -r, all with
|
|
73
|
+
// grep semantics. Patterns go over stdin so terms never reach the shell.
|
|
74
|
+
// -m stops each member early; one extra match per member reveals whether
|
|
75
|
+
// the cap cut anything off. The total across members is bounded by head,
|
|
76
|
+
// which ends the grep as soon as enough lines have been produced.
|
|
77
|
+
const flags = `-F -m ${maxMatches + 1} -f -`;
|
|
78
|
+
const modeFlags = options?.caseSensitive ? "-nHr" : "-inHr";
|
|
79
|
+
const outputLineLimit = (maxMatches + 1) * (before + after + 2);
|
|
80
|
+
const grep = connection.remoteFeatures.grep || "/usr/bin/grep";
|
|
81
|
+
const searchCommand = pfgrep
|
|
82
|
+
? `${pfgrep} ${modeFlags} ${contextFlags} ${flags} ${memberFilter}`
|
|
83
|
+
: `${grep} ${options?.caseSensitive ? "-nHR" : "-inHR"} ${contextFlags} ${flags} ${memberFilter}`;
|
|
84
|
+
const command = `${searchCommand.replace(/\s+/g, " ")} | /QOpenSys/usr/bin/head -n ${outputLineLimit}`;
|
|
51
85
|
const result = await connection.sendCommand({
|
|
52
86
|
command,
|
|
53
|
-
directory: `/QSYS.LIB/${lib}.LIB/${src}.FILE
|
|
87
|
+
directory: `/QSYS.LIB/${lib}.LIB/${src}.FILE`,
|
|
88
|
+
stdin: terms.join("\n")
|
|
54
89
|
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
90
|
+
const stdout = result.stdout || "";
|
|
91
|
+
if (result.stderr && !stdout.trim()) {
|
|
92
|
+
throw new Error(result.stderr || "Search failed");
|
|
93
|
+
}
|
|
94
|
+
const outputLines = stdout.split("\n").filter(line => line.length > 0);
|
|
95
|
+
// pfgrep prints after-context lines with ":" like matches, so tell them
|
|
96
|
+
// apart by whether the line contains one of the (literal) terms.
|
|
97
|
+
const isMatch = pfgrep && after > 0
|
|
98
|
+
? (content) => terms.some(term => options?.caseSensitive ? content.includes(term) : content.toLowerCase().includes(term.toLowerCase()))
|
|
99
|
+
: undefined;
|
|
100
|
+
const parsed = parseMemberGrepOutput(stdout, maxMatches, readOnly, isMatch);
|
|
101
|
+
let hits = parsed.hits;
|
|
102
|
+
if (detailedMembers) {
|
|
103
|
+
hits = hits.filter(hit => {
|
|
104
|
+
const hitMember = parseHitPath(hit);
|
|
105
|
+
return detailedMembers.some(member => member.name === hitMember.name && member.library === hitMember.library && member.file === hitMember.file);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const warnings = [];
|
|
109
|
+
if (pfgrep && requestedBefore > 0)
|
|
110
|
+
warnings.push("pfgrep does not support before-context lines, so only after-context lines are returned.");
|
|
111
|
+
if (result.stderr)
|
|
112
|
+
warnings.push(String(result.stderr).trim());
|
|
113
|
+
const outputLimited = outputLines.length >= outputLineLimit;
|
|
114
|
+
const truncated = parsed.truncated || outputLimited;
|
|
115
|
+
return {
|
|
116
|
+
term: terms[0],
|
|
117
|
+
terms,
|
|
118
|
+
hits,
|
|
119
|
+
matchCount: parsed.matchCount,
|
|
120
|
+
maxMatches,
|
|
121
|
+
truncated,
|
|
122
|
+
...(truncated ? { truncatedReason: parsed.truncated ? "max-matches" : "output-limit" } : {}),
|
|
123
|
+
...(warnings.length ? { warnings } : {})
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
Search.searchMembersTerms = searchMembersTerms;
|
|
127
|
+
// Parses grep-style output where matching lines use "path:line:text" and
|
|
128
|
+
// context lines use "path-line-text". Keeps at most maxMatches matches and
|
|
129
|
+
// the context that belongs to them.
|
|
130
|
+
function parseMemberGrepOutput(output, maxMatches, readonly, isMatch) {
|
|
131
|
+
const hits = [];
|
|
132
|
+
let matchCount = 0;
|
|
133
|
+
let truncated = false;
|
|
134
|
+
for (const line of output.split("\n")) {
|
|
135
|
+
if (!line || line === "--" || line.startsWith("Binary"))
|
|
136
|
+
continue;
|
|
137
|
+
const parsed = /^(.*?)([:-])(\d+)\2(.*)$/.exec(line);
|
|
138
|
+
if (!parsed)
|
|
139
|
+
continue;
|
|
140
|
+
const kind = parsed[2] === ":" && (!isMatch || isMatch(parsed[4])) ? "match" : "context";
|
|
141
|
+
if (kind === "match") {
|
|
142
|
+
if (matchCount >= maxMatches) {
|
|
143
|
+
truncated = true;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
matchCount++;
|
|
62
147
|
}
|
|
63
|
-
|
|
148
|
+
const path = parsed[1];
|
|
149
|
+
let hit = hits.find(entry => entry.path === path);
|
|
150
|
+
if (!hit) {
|
|
151
|
+
hit = { path, lines: [], readonly };
|
|
152
|
+
hits.push(hit);
|
|
153
|
+
}
|
|
154
|
+
hit.lines.push({ number: Number(parsed[3]), content: parsed[4], kind });
|
|
64
155
|
}
|
|
65
|
-
|
|
156
|
+
return { hits, matchCount, truncated };
|
|
157
|
+
}
|
|
158
|
+
function clampCount(value, minimum, maximum, fallback) {
|
|
159
|
+
const parsed = Number(value);
|
|
160
|
+
if (value === undefined || value === null || !Number.isFinite(parsed))
|
|
161
|
+
return fallback;
|
|
162
|
+
return Math.max(minimum, Math.min(maximum, Math.floor(parsed)));
|
|
66
163
|
}
|
|
67
|
-
Search.searchMembers = searchMembers;
|
|
68
164
|
async function searchIFS(connection, path, searchTerm) {
|
|
69
165
|
const grep = connection.remoteFeatures.grep;
|
|
70
166
|
if (!grep)
|
|
@@ -73,13 +169,10 @@ export var Search;
|
|
|
73
169
|
command: `${grep} -inr -F -f - ${Tools.escapePath(path)}`,
|
|
74
170
|
stdin: searchTerm
|
|
75
171
|
});
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (grepRes.code === 1 && !grepRes.stderr) {
|
|
81
|
-
return { term: searchTerm, hits: [] };
|
|
82
|
-
}
|
|
172
|
+
// grep exits 1 when nothing matched and 2 when any file or directory could
|
|
173
|
+
// not be read, even if other files matched. Keep whatever was printed and
|
|
174
|
+
// report the problems as warnings instead of discarding every hit.
|
|
175
|
+
return withIfsWarnings({ term: searchTerm, hits: parseGrepOutput(grepRes.stdout || ""), exitCode: grepRes.code ?? 0 }, grepRes.stderr);
|
|
83
176
|
}
|
|
84
177
|
Search.searchIFS = searchIFS;
|
|
85
178
|
async function findIFS(connection, path, findTerm) {
|
|
@@ -89,55 +182,68 @@ export var Search;
|
|
|
89
182
|
const findRes = await connection.sendCommand({
|
|
90
183
|
command: `${find} ${Tools.escapePath(path)} -type f -iname ${Tools.shellQuote(`*${findTerm}*`)} -print`
|
|
91
184
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
185
|
+
// find exits 1 when a single directory is unreadable; the paths it did
|
|
186
|
+
// print are still valid results.
|
|
187
|
+
return withIfsWarnings({ term: findTerm, hits: parseFindOutput(findRes.stdout || ""), exitCode: findRes.code ?? 0 }, findRes.stderr);
|
|
188
|
+
}
|
|
189
|
+
Search.findIFS = findIFS;
|
|
190
|
+
const MAX_IFS_WARNINGS = 50;
|
|
191
|
+
function withIfsWarnings(results, stderr) {
|
|
192
|
+
const lines = String(stderr || "")
|
|
193
|
+
.split(/\r?\n/)
|
|
194
|
+
.map(line => line.trim())
|
|
195
|
+
.filter(line => line.length > 0);
|
|
196
|
+
if (lines.length > 0) {
|
|
197
|
+
const warnings = lines.slice(0, MAX_IFS_WARNINGS);
|
|
198
|
+
if (lines.length > MAX_IFS_WARNINGS) {
|
|
199
|
+
warnings.push(`${lines.length - MAX_IFS_WARNINGS} more warning line(s) omitted`);
|
|
95
200
|
}
|
|
96
|
-
|
|
201
|
+
results.warnings = warnings;
|
|
97
202
|
}
|
|
203
|
+
return results;
|
|
98
204
|
}
|
|
99
|
-
Search.findIFS = findIFS;
|
|
100
205
|
function parseFindOutput(output, readonly, pathTransformer) {
|
|
101
206
|
const results = [];
|
|
102
|
-
|
|
207
|
+
const seen = new Set();
|
|
208
|
+
for (const rawLine of output.split('\n')) {
|
|
209
|
+
const line = rawLine.replace(/\r$/, "");
|
|
210
|
+
if (!line.trim())
|
|
211
|
+
continue;
|
|
103
212
|
const path = pathTransformer?.(line) || line;
|
|
104
|
-
|
|
213
|
+
if (seen.has(path))
|
|
214
|
+
continue;
|
|
215
|
+
seen.add(path);
|
|
216
|
+
results.push({ path, readonly, lines: [] });
|
|
105
217
|
}
|
|
106
218
|
return results;
|
|
107
219
|
}
|
|
108
220
|
function parseGrepOutput(output, readonly, pathTransformer) {
|
|
109
221
|
const results = [];
|
|
110
222
|
for (const line of output.split('\n')) {
|
|
111
|
-
if (line && !line.startsWith(`Binary`)) {
|
|
112
|
-
const
|
|
113
|
-
|
|
223
|
+
if (line && line !== "--" && !line.startsWith(`Binary`)) {
|
|
224
|
+
const parsed = /^(.*?)([:-])(\d+)\2(.*)$/.exec(line);
|
|
225
|
+
if (!parsed)
|
|
226
|
+
continue;
|
|
227
|
+
const path = pathTransformer?.(parsed[1]) || parsed[1];
|
|
114
228
|
let result = results.find(r => r.path === path);
|
|
115
229
|
if (!result) {
|
|
116
230
|
result = { path, lines: [], readonly };
|
|
117
231
|
results.push(result);
|
|
118
232
|
}
|
|
119
|
-
|
|
120
|
-
if (contentIndex >= 0) {
|
|
121
|
-
const curContent = line.substring(contentIndex + 1);
|
|
122
|
-
result.lines.push({ number: Number(parts[1]), content: curContent });
|
|
123
|
-
}
|
|
233
|
+
result.lines.push({ number: Number(parsed[3]), content: parsed[4] });
|
|
124
234
|
}
|
|
125
235
|
}
|
|
126
236
|
return results;
|
|
127
237
|
}
|
|
128
|
-
function sanitizeSearchTerm(searchTerm) {
|
|
129
|
-
return searchTerm.replace(/\\/g, `\\\\`).replace(/"/g, `\\"`);
|
|
130
|
-
}
|
|
131
238
|
function isSafeMemberToken(name) {
|
|
132
|
-
|
|
239
|
+
// IBM i names may contain $, # and @ (and . or _ after the first
|
|
240
|
+
// character); '*' is kept for generic filters such as ABC*.
|
|
241
|
+
return /^[A-Za-z0-9_.*#@$]{1,11}$/.test(name);
|
|
133
242
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
return index;
|
|
243
|
+
// Builds the shell word for a member glob. A leading '#' would start a shell
|
|
244
|
+
// comment and '$' would expand a variable, so both are escaped; '*' stays
|
|
245
|
+
// unescaped so generic names still expand.
|
|
246
|
+
function memberGlobToken(name) {
|
|
247
|
+
return `${name.replace(/[#$]/g, c => `\\${c}`)}.MBR`;
|
|
142
248
|
}
|
|
143
249
|
})(Search || (Search = {}));
|
package/dist/ibmi/Tools.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
+
import { rewriteSqlForQzdfmdb2 } from "./sqlParse.js";
|
|
2
3
|
export var Tools;
|
|
3
4
|
(function (Tools) {
|
|
4
5
|
class SqlError extends Error {
|
|
@@ -185,17 +186,13 @@ export var Tools;
|
|
|
185
186
|
return { library: parts[0], name: parts[1] };
|
|
186
187
|
}
|
|
187
188
|
Tools.parseQSysPath = parseQSysPath;
|
|
189
|
+
/**
|
|
190
|
+
* Prepares SQL for QZDFMDB2: lines starting with `@` become QCMDEXC calls and,
|
|
191
|
+
* with removeComments, `--` comments are dropped. Quote-aware: `--` inside a
|
|
192
|
+
* string literal, delimited identifier or block comment is kept.
|
|
193
|
+
*/
|
|
188
194
|
function fixSQL(statement, removeComments = false) {
|
|
189
|
-
|
|
190
|
-
if (line.startsWith('@')) {
|
|
191
|
-
line = `Call QSYS2.QCMDEXC('${line.substring(1, line.endsWith(";") ? line.length - 1 : undefined).replaceAll("'", "''")}');`;
|
|
192
|
-
}
|
|
193
|
-
return line.replaceAll("--", "\n--");
|
|
194
|
-
}).join(`\n`);
|
|
195
|
-
if (removeComments) {
|
|
196
|
-
statements = statements.split(`\n`).filter(l => !l.trim().startsWith(`--`)).join(`\n`);
|
|
197
|
-
}
|
|
198
|
-
return statements;
|
|
195
|
+
return rewriteSqlForQzdfmdb2(statement, removeComments);
|
|
199
196
|
}
|
|
200
197
|
Tools.fixSQL = fixSQL;
|
|
201
198
|
function upperCaseName(value) {
|