mcp-perforce-server 3.0.0 → 3.2.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.
@@ -68,6 +68,7 @@ exports.p4Fstat = p4Fstat;
68
68
  exports.p4Streams = p4Streams;
69
69
  exports.p4Stream = p4Stream;
70
70
  const parse = __importStar(require("../p4/parse.js"));
71
+ const arg_utils_js_1 = require("./arg-utils.js");
71
72
  /**
72
73
  * Input validation utilities
73
74
  */
@@ -160,11 +161,31 @@ async function p4Info(context, args = {}) {
160
161
  * p4 opened - List opened files
161
162
  */
162
163
  async function p4Opened(context, args = {}) {
164
+ if (args.files) {
165
+ const fileValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, args.files, 'filespec', 'files');
166
+ if (!fileValidation.valid) {
167
+ return {
168
+ ok: false,
169
+ command: 'opened',
170
+ args: [],
171
+ cwd: process.cwd(),
172
+ configUsed: {},
173
+ error: {
174
+ code: 'P4_INVALID_ARGS',
175
+ message: fileValidation.error || 'Invalid files',
176
+ },
177
+ };
178
+ }
179
+ }
163
180
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
164
181
  const cmdArgs = [];
165
182
  if (args.changelist) {
166
183
  cmdArgs.push('-c', args.changelist);
167
184
  }
185
+ if (args.files && args.files.length > 0) {
186
+ const fileValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, args.files, 'filespec', 'files');
187
+ cmdArgs.push(...(fileValidation.values || []));
188
+ }
168
189
  const result = await context.runner.run('opened', cmdArgs, cwd, {
169
190
  env,
170
191
  useZtag: false,
@@ -468,6 +489,50 @@ async function p4Revert(context, args = {}) {
468
489
  * p4 sync - Sync files from depot
469
490
  */
470
491
  async function p4Sync(context, args = {}) {
492
+ const filespecs = (0, arg_utils_js_1.mergeStringArgs)(args.filespec, args.filespecs);
493
+ if (filespecs.length > 0) {
494
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
495
+ if (!filespecValidation.valid) {
496
+ return {
497
+ ok: false,
498
+ command: 'sync',
499
+ args: [],
500
+ cwd: process.cwd(),
501
+ configUsed: {},
502
+ error: {
503
+ code: 'P4_INVALID_ARGS',
504
+ message: filespecValidation.error || 'Invalid filespecs',
505
+ },
506
+ };
507
+ }
508
+ }
509
+ const modeFlags = [args.metadataOnly, args.safeSync, args.populateOnly].filter(Boolean).length;
510
+ if (modeFlags > 1) {
511
+ return {
512
+ ok: false,
513
+ command: 'sync',
514
+ args: [],
515
+ cwd: process.cwd(),
516
+ configUsed: {},
517
+ error: {
518
+ code: 'P4_INVALID_ARGS',
519
+ message: 'metadataOnly, safeSync, and populateOnly are mutually exclusive',
520
+ },
521
+ };
522
+ }
523
+ if (args.preview && args.summaryPreview) {
524
+ return {
525
+ ok: false,
526
+ command: 'sync',
527
+ args: [],
528
+ cwd: process.cwd(),
529
+ configUsed: {},
530
+ error: {
531
+ code: 'P4_INVALID_ARGS',
532
+ message: 'preview and summaryPreview cannot both be true',
533
+ },
534
+ };
535
+ }
471
536
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
472
537
  const cmdArgs = [];
473
538
  if (args.force) {
@@ -476,8 +541,36 @@ async function p4Sync(context, args = {}) {
476
541
  if (args.preview) {
477
542
  cmdArgs.push('-n');
478
543
  }
479
- if (args.filespec) {
480
- cmdArgs.push(args.filespec);
544
+ if (args.summaryPreview) {
545
+ cmdArgs.push('-N');
546
+ }
547
+ if (args.quiet) {
548
+ cmdArgs.push('-q');
549
+ }
550
+ if (args.metadataOnly) {
551
+ cmdArgs.push('-k');
552
+ }
553
+ if (args.safeSync) {
554
+ cmdArgs.push('-s');
555
+ }
556
+ if (args.populateOnly) {
557
+ cmdArgs.push('-p');
558
+ }
559
+ if (args.reopenMoved) {
560
+ cmdArgs.push('-r');
561
+ }
562
+ if (args.useListOptimization) {
563
+ cmdArgs.push('-L');
564
+ }
565
+ if (args.max && args.max > 0) {
566
+ cmdArgs.push('-m', args.max.toString());
567
+ }
568
+ if (args.parallel) {
569
+ cmdArgs.push(`--parallel=${args.parallel}`);
570
+ }
571
+ if (filespecs.length > 0) {
572
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
573
+ cmdArgs.push(...(filespecValidation.values || []));
481
574
  }
482
575
  const result = await context.runner.run('sync', cmdArgs, cwd, {
483
576
  env,
@@ -784,6 +877,23 @@ async function p4Unshelve(context, args) {
784
877
  * p4 changes - List submitted changelists (enhanced version)
785
878
  */
786
879
  async function p4Changes(context, args = {}) {
880
+ const filespecs = (0, arg_utils_js_1.mergeStringArgs)(args.filespec, args.filespecs);
881
+ if (filespecs.length > 0) {
882
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
883
+ if (!filespecValidation.valid) {
884
+ return {
885
+ ok: false,
886
+ command: 'changes',
887
+ args: [],
888
+ cwd: process.cwd(),
889
+ configUsed: {},
890
+ error: {
891
+ code: 'P4_INVALID_ARGS',
892
+ message: filespecValidation.error || 'Invalid filespecs',
893
+ },
894
+ };
895
+ }
896
+ }
787
897
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
788
898
  const cmdArgs = [];
789
899
  // Add status filter
@@ -803,8 +913,9 @@ async function p4Changes(context, args = {}) {
803
913
  cmdArgs.push('-m', args.max.toString());
804
914
  }
805
915
  // Add filespec
806
- if (args.filespec) {
807
- cmdArgs.push(args.filespec);
916
+ if (filespecs.length > 0) {
917
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
918
+ cmdArgs.push(...(filespecValidation.values || []));
808
919
  }
809
920
  const result = await context.runner.run('changes', cmdArgs, cwd, {
810
921
  env,
@@ -976,7 +1087,10 @@ async function p4Reviews(context, args = {}) {
976
1087
  * p4 interchanges - List changelists not yet integrated between paths
977
1088
  */
978
1089
  async function p4Interchanges(context, args) {
979
- if (!args.sourcePath || !args.targetPath) {
1090
+ const targetPaths = (0, arg_utils_js_1.mergeStringArgs)(args.targetPath, args.targetPaths);
1091
+ const usingPathMode = !args.branch && !args.stream && !!args.sourcePath && targetPaths.length > 0;
1092
+ const modeCount = [usingPathMode, !!args.branch, !!args.stream].filter(Boolean).length;
1093
+ if (modeCount !== 1) {
980
1094
  return {
981
1095
  ok: false,
982
1096
  command: 'interchanges',
@@ -985,13 +1099,71 @@ async function p4Interchanges(context, args) {
985
1099
  configUsed: {},
986
1100
  error: {
987
1101
  code: 'P4_INVALID_ARGS',
988
- message: 'sourcePath and targetPath parameters are required',
1102
+ message: 'Provide exactly one interchanges mode: sourcePath+targetPath(s), branch, or stream',
989
1103
  },
990
1104
  };
991
1105
  }
992
- const sourceSanitization = context.security.sanitizeInput(args.sourcePath, 'filespec');
993
- const targetSanitization = context.security.sanitizeInput(args.targetPath, 'filespec');
994
- if (!sourceSanitization.valid || !targetSanitization.valid) {
1106
+ if (args.useBranchSource && !args.branch) {
1107
+ return {
1108
+ ok: false,
1109
+ command: 'interchanges',
1110
+ args: [],
1111
+ cwd: process.cwd(),
1112
+ configUsed: {},
1113
+ error: {
1114
+ code: 'P4_INVALID_ARGS',
1115
+ message: 'useBranchSource requires branch mode',
1116
+ },
1117
+ };
1118
+ }
1119
+ if (args.parentStream && !args.stream) {
1120
+ return {
1121
+ ok: false,
1122
+ command: 'interchanges',
1123
+ args: [],
1124
+ cwd: process.cwd(),
1125
+ configUsed: {},
1126
+ error: {
1127
+ code: 'P4_INVALID_ARGS',
1128
+ message: 'parentStream requires stream mode',
1129
+ },
1130
+ };
1131
+ }
1132
+ if (args.forceStreamFlow && !args.stream) {
1133
+ return {
1134
+ ok: false,
1135
+ command: 'interchanges',
1136
+ args: [],
1137
+ cwd: process.cwd(),
1138
+ configUsed: {},
1139
+ error: {
1140
+ code: 'P4_INVALID_ARGS',
1141
+ message: 'forceStreamFlow requires stream mode',
1142
+ },
1143
+ };
1144
+ }
1145
+ let sourceSanitized;
1146
+ if (args.sourcePath) {
1147
+ const sourceSanitization = context.security.sanitizeInput(args.sourcePath, 'filespec');
1148
+ if (!sourceSanitization.valid) {
1149
+ return {
1150
+ ok: false,
1151
+ command: 'interchanges',
1152
+ args: [],
1153
+ cwd: process.cwd(),
1154
+ configUsed: {},
1155
+ error: {
1156
+ code: 'P4_INVALID_ARGS',
1157
+ message: `Invalid sourcePath: ${sourceSanitization.warnings.join(', ')}`,
1158
+ },
1159
+ };
1160
+ }
1161
+ sourceSanitized = sourceSanitization.sanitized;
1162
+ }
1163
+ const targetValidation = targetPaths.length > 0
1164
+ ? (0, arg_utils_js_1.sanitizeStringList)(context.security, targetPaths, 'filespec', 'targetPaths')
1165
+ : { valid: true, values: [] };
1166
+ if (!targetValidation.valid) {
995
1167
  return {
996
1168
  ok: false,
997
1169
  command: 'interchanges',
@@ -1000,7 +1172,7 @@ async function p4Interchanges(context, args) {
1000
1172
  configUsed: {},
1001
1173
  error: {
1002
1174
  code: 'P4_INVALID_ARGS',
1003
- message: 'Invalid sourcePath or targetPath filespec',
1175
+ message: targetValidation.error || 'Invalid targetPaths',
1004
1176
  },
1005
1177
  };
1006
1178
  }
@@ -1012,7 +1184,55 @@ async function p4Interchanges(context, args) {
1012
1184
  if (args.max && args.max > 0) {
1013
1185
  cmdArgs.push('-m', args.max.toString());
1014
1186
  }
1015
- cmdArgs.push(sourceSanitization.sanitized, targetSanitization.sanitized);
1187
+ if (args.reverse) {
1188
+ cmdArgs.push('-r');
1189
+ }
1190
+ if (args.time) {
1191
+ cmdArgs.push('-t');
1192
+ }
1193
+ if (args.user) {
1194
+ cmdArgs.push('-u', args.user);
1195
+ }
1196
+ if (args.stream) {
1197
+ cmdArgs.push('-S', args.stream);
1198
+ if (args.parentStream) {
1199
+ cmdArgs.push('-P', args.parentStream);
1200
+ }
1201
+ if (args.forceStreamFlow) {
1202
+ cmdArgs.push('-F');
1203
+ }
1204
+ if (targetValidation.values && targetValidation.values.length > 0) {
1205
+ cmdArgs.push(...targetValidation.values);
1206
+ }
1207
+ }
1208
+ else if (args.branch) {
1209
+ cmdArgs.push('-b', args.branch);
1210
+ if (args.useBranchSource) {
1211
+ if (!sourceSanitized) {
1212
+ return {
1213
+ ok: false,
1214
+ command: 'interchanges',
1215
+ args: [],
1216
+ cwd: process.cwd(),
1217
+ configUsed: {},
1218
+ error: {
1219
+ code: 'P4_INVALID_ARGS',
1220
+ message: 'sourcePath is required when useBranchSource is true',
1221
+ },
1222
+ };
1223
+ }
1224
+ cmdArgs.push('-s', sourceSanitized);
1225
+ if (targetValidation.values && targetValidation.values.length > 0) {
1226
+ cmdArgs.push(...targetValidation.values);
1227
+ }
1228
+ }
1229
+ else if (targetValidation.values && targetValidation.values.length > 0) {
1230
+ cmdArgs.push(...targetValidation.values);
1231
+ }
1232
+ }
1233
+ else {
1234
+ cmdArgs.push(sourceSanitized, ...(targetValidation.values || []));
1235
+ }
1016
1236
  const result = await context.runner.run('interchanges', cmdArgs, cwd, {
1017
1237
  env,
1018
1238
  useZtag: false,
@@ -1039,7 +1259,7 @@ async function p4Interchanges(context, args) {
1039
1259
  * p4 integrated - Show integration history between paths
1040
1260
  */
1041
1261
  async function p4Integrated(context, args) {
1042
- if (!args.sourcePath) {
1262
+ if (!args.sourcePath && (!args.files || args.files.length === 0)) {
1043
1263
  return {
1044
1264
  ok: false,
1045
1265
  command: 'integrated',
@@ -1048,23 +1268,27 @@ async function p4Integrated(context, args) {
1048
1268
  configUsed: {},
1049
1269
  error: {
1050
1270
  code: 'P4_INVALID_ARGS',
1051
- message: 'sourcePath parameter is required',
1271
+ message: 'sourcePath or files parameter is required',
1052
1272
  },
1053
1273
  };
1054
1274
  }
1055
- const sourceSanitization = context.security.sanitizeInput(args.sourcePath, 'filespec');
1056
- if (!sourceSanitization.valid) {
1057
- return {
1058
- ok: false,
1059
- command: 'integrated',
1060
- args: [],
1061
- cwd: process.cwd(),
1062
- configUsed: {},
1063
- error: {
1064
- code: 'P4_INVALID_ARGS',
1065
- message: `Invalid sourcePath: ${sourceSanitization.warnings.join(', ')}`,
1066
- },
1067
- };
1275
+ let sourceSanitized;
1276
+ if (args.sourcePath) {
1277
+ const sourceSanitization = context.security.sanitizeInput(args.sourcePath, 'filespec');
1278
+ if (!sourceSanitization.valid) {
1279
+ return {
1280
+ ok: false,
1281
+ command: 'integrated',
1282
+ args: [],
1283
+ cwd: process.cwd(),
1284
+ configUsed: {},
1285
+ error: {
1286
+ code: 'P4_INVALID_ARGS',
1287
+ message: `Invalid sourcePath: ${sourceSanitization.warnings.join(', ')}`,
1288
+ },
1289
+ };
1290
+ }
1291
+ sourceSanitized = sourceSanitization.sanitized;
1068
1292
  }
1069
1293
  let targetSanitized;
1070
1294
  if (args.targetPath) {
@@ -1084,10 +1308,34 @@ async function p4Integrated(context, args) {
1084
1308
  }
1085
1309
  targetSanitized = targetSanitization.sanitized;
1086
1310
  }
1311
+ let filesSanitized = [];
1312
+ if (args.files && args.files.length > 0) {
1313
+ const fileValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, args.files, 'filespec', 'files');
1314
+ if (!fileValidation.valid) {
1315
+ return {
1316
+ ok: false,
1317
+ command: 'integrated',
1318
+ args: [],
1319
+ cwd: process.cwd(),
1320
+ configUsed: {},
1321
+ error: {
1322
+ code: 'P4_INVALID_ARGS',
1323
+ message: fileValidation.error || 'Invalid files',
1324
+ },
1325
+ };
1326
+ }
1327
+ filesSanitized = fileValidation.values || [];
1328
+ }
1087
1329
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
1088
- const cmdArgs = [sourceSanitization.sanitized];
1089
- if (targetSanitized) {
1090
- cmdArgs.push(targetSanitized);
1330
+ const cmdArgs = [];
1331
+ if (sourceSanitized) {
1332
+ cmdArgs.push(sourceSanitized);
1333
+ if (targetSanitized) {
1334
+ cmdArgs.push(targetSanitized);
1335
+ }
1336
+ }
1337
+ if (filesSanitized.length > 0) {
1338
+ cmdArgs.push(...filesSanitized);
1091
1339
  }
1092
1340
  const result = await context.runner.run('integrated', cmdArgs, cwd, {
1093
1341
  env,
@@ -1115,7 +1363,22 @@ async function p4Integrated(context, args) {
1115
1363
  * p4 blame - Show file annotations with change history
1116
1364
  */
1117
1365
  async function p4Blame(context, args) {
1118
- if (!args.file) {
1366
+ const files = (0, arg_utils_js_1.mergeStringArgs)(args.file, args.files);
1367
+ if (files.length === 0) {
1368
+ return {
1369
+ ok: false,
1370
+ command: 'blame',
1371
+ args: [],
1372
+ cwd: process.cwd(),
1373
+ configUsed: {},
1374
+ error: {
1375
+ code: 'P4_INVALID_ARGS',
1376
+ message: 'file or files parameter is required',
1377
+ },
1378
+ };
1379
+ }
1380
+ const fileValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, files, 'filespec', 'files');
1381
+ if (!fileValidation.valid) {
1119
1382
  return {
1120
1383
  ok: false,
1121
1384
  command: 'blame',
@@ -1124,12 +1387,12 @@ async function p4Blame(context, args) {
1124
1387
  configUsed: {},
1125
1388
  error: {
1126
1389
  code: 'P4_INVALID_ARGS',
1127
- message: 'file parameter is required',
1390
+ message: fileValidation.error || 'Invalid files',
1128
1391
  },
1129
1392
  };
1130
1393
  }
1131
1394
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
1132
- const result = await context.runner.run('annotate', ['-a', args.file], cwd, {
1395
+ const result = await context.runner.run('annotate', ['-a', ...(fileValidation.values || [])], cwd, {
1133
1396
  env,
1134
1397
  useZtag: false,
1135
1398
  parseOutput: false,
@@ -1343,10 +1606,10 @@ async function p4Grep(context, args) {
1343
1606
  },
1344
1607
  };
1345
1608
  }
1346
- let sanitizedFilespec = args.filespec;
1347
- if (args.filespec) {
1348
- const filespecSanitization = context.security.sanitizeInput(args.filespec, 'filespec');
1349
- if (!filespecSanitization.valid) {
1609
+ const filespecs = (0, arg_utils_js_1.mergeStringArgs)(args.filespec, args.filespecs);
1610
+ if (filespecs.length > 0) {
1611
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
1612
+ if (!filespecValidation.valid) {
1350
1613
  return {
1351
1614
  ok: false,
1352
1615
  command: 'grep',
@@ -1355,11 +1618,10 @@ async function p4Grep(context, args) {
1355
1618
  configUsed: {},
1356
1619
  error: {
1357
1620
  code: 'P4_INVALID_ARGS',
1358
- message: `Invalid filespec: ${filespecSanitization.warnings.join(', ')}`,
1621
+ message: filespecValidation.error || 'Invalid filespecs',
1359
1622
  },
1360
1623
  };
1361
1624
  }
1362
- sanitizedFilespec = filespecSanitization.sanitized;
1363
1625
  }
1364
1626
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
1365
1627
  const cmdArgs = [];
@@ -1367,8 +1629,9 @@ async function p4Grep(context, args) {
1367
1629
  cmdArgs.push('-i');
1368
1630
  }
1369
1631
  cmdArgs.push('-e', patternSanitization.sanitized);
1370
- if (sanitizedFilespec) {
1371
- cmdArgs.push(sanitizedFilespec);
1632
+ if (filespecs.length > 0) {
1633
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
1634
+ cmdArgs.push(...(filespecValidation.values || []));
1372
1635
  }
1373
1636
  const result = await context.runner.run('grep', cmdArgs, cwd, {
1374
1637
  env,
@@ -1388,11 +1651,10 @@ async function p4Grep(context, args) {
1388
1651
  * p4 files - List files in depot with metadata
1389
1652
  */
1390
1653
  async function p4Files(context, args) {
1391
- // Input sanitization for filespec
1392
- let sanitizedFilespec = args.filespec || '...';
1393
- if (args.filespec) {
1394
- const filespecSanitization = context.security.sanitizeInput(args.filespec, 'filespec');
1395
- if (!filespecSanitization.valid) {
1654
+ const filespecs = (0, arg_utils_js_1.mergeStringArgs)(args.filespec, args.filespecs);
1655
+ if (filespecs.length > 0) {
1656
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
1657
+ if (!filespecValidation.valid) {
1396
1658
  return {
1397
1659
  ok: false,
1398
1660
  command: 'files',
@@ -1401,18 +1663,23 @@ async function p4Files(context, args) {
1401
1663
  configUsed: {},
1402
1664
  error: {
1403
1665
  code: 'P4_INVALID_ARGS',
1404
- message: `Invalid filespec: ${filespecSanitization.warnings.join(', ')}`,
1666
+ message: filespecValidation.error || 'Invalid filespecs',
1405
1667
  },
1406
1668
  };
1407
1669
  }
1408
- sanitizedFilespec = filespecSanitization.sanitized;
1409
1670
  }
1410
1671
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
1411
1672
  const cmdArgs = [];
1412
1673
  if (args.max) {
1413
1674
  cmdArgs.push('-m', args.max.toString());
1414
1675
  }
1415
- cmdArgs.push(sanitizedFilespec);
1676
+ if (filespecs.length > 0) {
1677
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
1678
+ cmdArgs.push(...(filespecValidation.values || []));
1679
+ }
1680
+ else {
1681
+ cmdArgs.push('...');
1682
+ }
1416
1683
  const result = await context.runner.run('files', cmdArgs, cwd, {
1417
1684
  env,
1418
1685
  useZtag: false,
@@ -1431,11 +1698,10 @@ async function p4Files(context, args) {
1431
1698
  * p4 dirs - List directories in depot
1432
1699
  */
1433
1700
  async function p4Dirs(context, args) {
1434
- // Input sanitization for filespec
1435
- let sanitizedFilespec = args.filespec || '...';
1436
- if (args.filespec) {
1437
- const filespecSanitization = context.security.sanitizeInput(args.filespec, 'filespec');
1438
- if (!filespecSanitization.valid) {
1701
+ const filespecs = (0, arg_utils_js_1.mergeStringArgs)(args.filespec, args.filespecs);
1702
+ if (filespecs.length > 0) {
1703
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
1704
+ if (!filespecValidation.valid) {
1439
1705
  return {
1440
1706
  ok: false,
1441
1707
  command: 'dirs',
@@ -1444,15 +1710,48 @@ async function p4Dirs(context, args) {
1444
1710
  configUsed: {},
1445
1711
  error: {
1446
1712
  code: 'P4_INVALID_ARGS',
1447
- message: `Invalid filespec: ${filespecSanitization.warnings.join(', ')}`,
1713
+ message: filespecValidation.error || 'Invalid filespecs',
1448
1714
  },
1449
1715
  };
1450
1716
  }
1451
- sanitizedFilespec = filespecSanitization.sanitized;
1717
+ }
1718
+ if (args.ignoreCase && args.onlyClientMapped) {
1719
+ return {
1720
+ ok: false,
1721
+ command: 'dirs',
1722
+ args: [],
1723
+ cwd: process.cwd(),
1724
+ configUsed: {},
1725
+ error: {
1726
+ code: 'P4_INVALID_ARGS',
1727
+ message: 'ignoreCase and onlyClientMapped cannot be combined',
1728
+ },
1729
+ };
1452
1730
  }
1453
1731
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
1454
1732
  const cmdArgs = [];
1455
- cmdArgs.push(sanitizedFilespec);
1733
+ if (args.onlyClientMapped) {
1734
+ cmdArgs.push('-C');
1735
+ }
1736
+ if (args.includeDeleted) {
1737
+ cmdArgs.push('-D');
1738
+ }
1739
+ if (args.onlyHave) {
1740
+ cmdArgs.push('-H');
1741
+ }
1742
+ if (args.stream) {
1743
+ cmdArgs.push('-S', args.stream);
1744
+ }
1745
+ if (args.ignoreCase) {
1746
+ cmdArgs.push('-i');
1747
+ }
1748
+ if (filespecs.length > 0) {
1749
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
1750
+ cmdArgs.push(...(filespecValidation.values || []));
1751
+ }
1752
+ else {
1753
+ cmdArgs.push('...');
1754
+ }
1456
1755
  const result = await context.runner.run('dirs', cmdArgs, cwd, {
1457
1756
  env,
1458
1757
  useZtag: false,
@@ -1601,7 +1900,8 @@ async function p4Merge(context, args) {
1601
1900
  * p4 print - Print file contents from depot
1602
1901
  */
1603
1902
  async function p4Print(context, args) {
1604
- if (!args.filespec) {
1903
+ const filespecs = (0, arg_utils_js_1.mergeStringArgs)(args.filespec, args.filespecs);
1904
+ if (filespecs.length === 0) {
1605
1905
  return {
1606
1906
  ok: false,
1607
1907
  command: 'print',
@@ -1610,12 +1910,12 @@ async function p4Print(context, args) {
1610
1910
  configUsed: {},
1611
1911
  error: {
1612
1912
  code: 'P4_INVALID_ARGS',
1613
- message: 'filespec parameter is required',
1913
+ message: 'filespec or filespecs parameter is required',
1614
1914
  },
1615
1915
  };
1616
1916
  }
1617
- const filespecSanitization = context.security.sanitizeInput(args.filespec, 'filespec');
1618
- if (!filespecSanitization.valid) {
1917
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
1918
+ if (!filespecValidation.valid) {
1619
1919
  return {
1620
1920
  ok: false,
1621
1921
  command: 'print',
@@ -1624,7 +1924,7 @@ async function p4Print(context, args) {
1624
1924
  configUsed: {},
1625
1925
  error: {
1626
1926
  code: 'P4_INVALID_ARGS',
1627
- message: `Invalid filespec: ${filespecSanitization.warnings.join(', ')}`,
1927
+ message: filespecValidation.error || 'Invalid filespecs',
1628
1928
  },
1629
1929
  };
1630
1930
  }
@@ -1633,7 +1933,7 @@ async function p4Print(context, args) {
1633
1933
  if (args.quiet !== false) {
1634
1934
  cmdArgs.push('-q');
1635
1935
  }
1636
- cmdArgs.push(filespecSanitization.sanitized);
1936
+ cmdArgs.push(...(filespecValidation.values || []));
1637
1937
  const result = await context.runner.run('print', cmdArgs, cwd, {
1638
1938
  env,
1639
1939
  useZtag: false,
@@ -1652,7 +1952,8 @@ async function p4Print(context, args) {
1652
1952
  * p4 fstat - Show file metadata from depot/workspace
1653
1953
  */
1654
1954
  async function p4Fstat(context, args) {
1655
- if (!args.filespec) {
1955
+ const filespecs = (0, arg_utils_js_1.mergeStringArgs)(args.filespec, args.filespecs);
1956
+ if (filespecs.length === 0) {
1656
1957
  return {
1657
1958
  ok: false,
1658
1959
  command: 'fstat',
@@ -1661,12 +1962,12 @@ async function p4Fstat(context, args) {
1661
1962
  configUsed: {},
1662
1963
  error: {
1663
1964
  code: 'P4_INVALID_ARGS',
1664
- message: 'filespec parameter is required',
1965
+ message: 'filespec or filespecs parameter is required',
1665
1966
  },
1666
1967
  };
1667
1968
  }
1668
- const filespecSanitization = context.security.sanitizeInput(args.filespec, 'filespec');
1669
- if (!filespecSanitization.valid) {
1969
+ const filespecValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, filespecs, 'filespec', 'filespecs');
1970
+ if (!filespecValidation.valid) {
1670
1971
  return {
1671
1972
  ok: false,
1672
1973
  command: 'fstat',
@@ -1675,16 +1976,62 @@ async function p4Fstat(context, args) {
1675
1976
  configUsed: {},
1676
1977
  error: {
1677
1978
  code: 'P4_INVALID_ARGS',
1678
- message: `Invalid filespec: ${filespecSanitization.warnings.join(', ')}`,
1979
+ message: filespecValidation.error || 'Invalid filespecs',
1980
+ },
1981
+ };
1982
+ }
1983
+ if (args.changeAfter && args.changelist) {
1984
+ return {
1985
+ ok: false,
1986
+ command: 'fstat',
1987
+ args: [],
1988
+ cwd: process.cwd(),
1989
+ configUsed: {},
1990
+ error: {
1991
+ code: 'P4_INVALID_ARGS',
1992
+ message: 'changeAfter and changelist cannot both be specified',
1679
1993
  },
1680
1994
  };
1681
1995
  }
1682
1996
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
1683
1997
  const cmdArgs = [];
1998
+ if (args.filter) {
1999
+ cmdArgs.push('-F', args.filter);
2000
+ }
2001
+ if (args.fields && args.fields.length > 0) {
2002
+ cmdArgs.push('-T', args.fields.join(','));
2003
+ }
1684
2004
  if (args.max) {
1685
2005
  cmdArgs.push('-m', args.max.toString());
1686
2006
  }
1687
- cmdArgs.push(filespecSanitization.sanitized);
2007
+ if (args.reverseOrder) {
2008
+ cmdArgs.push('-r');
2009
+ }
2010
+ if (args.changeAfter) {
2011
+ cmdArgs.push('-c', args.changeAfter);
2012
+ }
2013
+ if (args.changelist) {
2014
+ cmdArgs.push('-e', args.changelist);
2015
+ }
2016
+ if (args.attributePattern) {
2017
+ cmdArgs.push('-A', args.attributePattern);
2018
+ }
2019
+ if (args.outputOptions) {
2020
+ for (const option of args.outputOptions) {
2021
+ cmdArgs.push(`-O${option}`);
2022
+ }
2023
+ }
2024
+ if (args.limitOptions) {
2025
+ for (const option of args.limitOptions) {
2026
+ cmdArgs.push(`-R${option}`);
2027
+ }
2028
+ }
2029
+ if (args.sortOptions) {
2030
+ for (const option of args.sortOptions) {
2031
+ cmdArgs.push(`-S${option}`);
2032
+ }
2033
+ }
2034
+ cmdArgs.push(...(filespecValidation.values || []));
1688
2035
  const result = await context.runner.run('fstat', cmdArgs, cwd, {
1689
2036
  env,
1690
2037
  useZtag: true,
@@ -1703,13 +2050,56 @@ async function p4Fstat(context, args) {
1703
2050
  * p4 streams - List streams
1704
2051
  */
1705
2052
  async function p4Streams(context, args = {}) {
2053
+ const streams = (0, arg_utils_js_1.mergeStringArgs)(args.stream, args.streams);
2054
+ if (streams.length > 0) {
2055
+ const streamValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, streams, 'filespec', 'streams');
2056
+ if (!streamValidation.valid) {
2057
+ return {
2058
+ ok: false,
2059
+ command: 'streams',
2060
+ args: [],
2061
+ cwd: process.cwd(),
2062
+ configUsed: {},
2063
+ error: {
2064
+ code: 'P4_INVALID_ARGS',
2065
+ message: streamValidation.error || 'Invalid streams',
2066
+ },
2067
+ };
2068
+ }
2069
+ }
1706
2070
  const { cwd, env, configResult } = await context.config.setupForCommand(args.workspacePath);
1707
2071
  const cmdArgs = [];
2072
+ if (args.unloaded) {
2073
+ cmdArgs.push('-U');
2074
+ }
2075
+ if (args.filter) {
2076
+ cmdArgs.push('-F', args.filter);
2077
+ }
1708
2078
  if (args.max) {
1709
2079
  cmdArgs.push('-m', args.max.toString());
1710
2080
  }
1711
- if (args.stream) {
1712
- cmdArgs.push(args.stream);
2081
+ if (args.viewMatch) {
2082
+ const viewMatchValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, args.viewMatch, 'filespec', 'viewMatch');
2083
+ if (!viewMatchValidation.valid) {
2084
+ return {
2085
+ ok: false,
2086
+ command: 'streams',
2087
+ args: [],
2088
+ cwd: process.cwd(),
2089
+ configUsed: {},
2090
+ error: {
2091
+ code: 'P4_INVALID_ARGS',
2092
+ message: viewMatchValidation.error || 'Invalid viewMatch',
2093
+ },
2094
+ };
2095
+ }
2096
+ for (const viewMatch of viewMatchValidation.values || []) {
2097
+ cmdArgs.push('--viewmatch', viewMatch);
2098
+ }
2099
+ }
2100
+ if (streams.length > 0) {
2101
+ const streamValidation = (0, arg_utils_js_1.sanitizeStringList)(context.security, streams, 'filespec', 'streams');
2102
+ cmdArgs.push(...(streamValidation.values || []));
1713
2103
  }
1714
2104
  const result = await context.runner.run('streams', cmdArgs, cwd, {
1715
2105
  env,