@vucinatim/agentic-devtools 0.1.2 → 0.1.4

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.
@@ -86,6 +86,16 @@ export const createRailwayClient = ({
86
86
  );
87
87
  };
88
88
 
89
+ const requireResolvedProjectId = async (projectId, operation) => {
90
+ try {
91
+ return await resolveProjectId(projectId);
92
+ } catch (error) {
93
+ throw new RailwayApiError(
94
+ error instanceof Error ? error.message : `${operation} requires a Railway project id.`,
95
+ );
96
+ }
97
+ };
98
+
89
99
  const getCurrentViewer = async () => {
90
100
  requireAccountToken("getRailwayViewer");
91
101
  const data = await request(`
@@ -103,6 +113,20 @@ export const createRailwayClient = ({
103
113
  return data.me;
104
114
  };
105
115
 
116
+ const validateAccountToken = async () => {
117
+ requireAccountToken("validateRailwayAccountToken");
118
+ const projects = await listProjects({ first: 1, includeDeleted: false });
119
+ return {
120
+ ok: true,
121
+ projectCountSampled: projects.length,
122
+ sampleProjects: projects.map((project) => ({
123
+ id: project.id,
124
+ name: project.name,
125
+ workspace: project.workspace?.name ?? null,
126
+ })),
127
+ };
128
+ };
129
+
106
130
  const listProjects = async ({
107
131
  workspaceId = null,
108
132
  includeDeleted = false,
@@ -217,6 +241,27 @@ export const createRailwayClient = ({
217
241
  };
218
242
  };
219
243
 
244
+ const getProjectMembers = async (projectId) => {
245
+ requireAccountToken("getRailwayProjectMembers");
246
+ const id = await requireResolvedProjectId(projectId, "getRailwayProjectMembers");
247
+ const data = await request(
248
+ `
249
+ query RailwayProjectMembers($projectId: String!) {
250
+ projectMembers(projectId: $projectId) {
251
+ id
252
+ role
253
+ user {
254
+ name
255
+ email
256
+ }
257
+ }
258
+ }
259
+ `,
260
+ { projectId: id },
261
+ );
262
+ return data.projectMembers;
263
+ };
264
+
220
265
  const listEnvironments = async ({ projectId, isEphemeral } = {}) => {
221
266
  const resolvedProjectId = await resolveProjectId(projectId);
222
267
  const data = await request(
@@ -294,6 +339,675 @@ export const createRailwayClient = ({
294
339
  };
295
340
  };
296
341
 
342
+ const getService = async (serviceId) => {
343
+ const data = await request(
344
+ `
345
+ query RailwayService($id: String!) {
346
+ service(id: $id) {
347
+ id
348
+ name
349
+ icon
350
+ createdAt
351
+ updatedAt
352
+ deletedAt
353
+ featureFlags
354
+ project {
355
+ id
356
+ name
357
+ }
358
+ }
359
+ }
360
+ `,
361
+ { id: serviceId },
362
+ );
363
+ return {
364
+ ...data.service,
365
+ projectId: data.service.project?.id ?? null,
366
+ projectName: data.service.project?.name ?? null,
367
+ };
368
+ };
369
+
370
+ const getServiceInstance = async ({ serviceId, environmentId }) => {
371
+ const data = await request(
372
+ `
373
+ query RailwayServiceInstance($serviceId: String!, $environmentId: String!) {
374
+ serviceInstance(serviceId: $serviceId, environmentId: $environmentId) {
375
+ id
376
+ serviceId
377
+ serviceName
378
+ environmentId
379
+ rootDirectory
380
+ railwayConfigFile
381
+ buildCommand
382
+ startCommand
383
+ healthcheckPath
384
+ cronSchedule
385
+ latestDeployment {
386
+ id
387
+ status
388
+ url
389
+ staticUrl
390
+ }
391
+ domains {
392
+ serviceDomains {
393
+ id
394
+ domain
395
+ }
396
+ customDomains {
397
+ id
398
+ domain
399
+ }
400
+ }
401
+ }
402
+ }
403
+ `,
404
+ { serviceId, environmentId },
405
+ );
406
+ return data.serviceInstance;
407
+ };
408
+
409
+ const getServiceInstanceLimits = async ({ serviceId, environmentId }) => {
410
+ const data = await request(
411
+ `
412
+ query RailwayServiceInstanceLimits($serviceId: String!, $environmentId: String!) {
413
+ serviceInstanceLimits(serviceId: $serviceId, environmentId: $environmentId)
414
+ }
415
+ `,
416
+ { serviceId, environmentId },
417
+ );
418
+ return data.serviceInstanceLimits;
419
+ };
420
+
421
+ const getDeployment = async (deploymentId) => {
422
+ const data = await request(
423
+ `
424
+ query RailwayDeployment($id: String!) {
425
+ deployment(id: $id) {
426
+ id
427
+ status
428
+ createdAt
429
+ updatedAt
430
+ statusUpdatedAt
431
+ canRedeploy
432
+ canRollback
433
+ deploymentStopped
434
+ environmentId
435
+ projectId
436
+ serviceId
437
+ url
438
+ staticUrl
439
+ service {
440
+ id
441
+ name
442
+ }
443
+ environment {
444
+ id
445
+ name
446
+ }
447
+ }
448
+ }
449
+ `,
450
+ { id: deploymentId },
451
+ );
452
+ return {
453
+ ...data.deployment,
454
+ serviceName: data.deployment.service?.name ?? null,
455
+ environmentName: data.deployment.environment?.name ?? null,
456
+ };
457
+ };
458
+
459
+ const listDeployments = async ({
460
+ projectId,
461
+ environmentId,
462
+ serviceId,
463
+ first = 20,
464
+ after = null,
465
+ before = null,
466
+ last = null,
467
+ } = {}) => {
468
+ const resolvedProjectId =
469
+ projectId == null && auth.kind === "project"
470
+ ? await requireResolvedProjectId(null, "listRailwayDeployments")
471
+ : projectId;
472
+
473
+ const input = compactObject({
474
+ projectId: resolvedProjectId,
475
+ environmentId,
476
+ serviceId,
477
+ });
478
+
479
+ const data = await request(
480
+ `
481
+ query RailwayDeployments(
482
+ $input: DeploymentListInput!
483
+ $first: Int
484
+ $after: String
485
+ $before: String
486
+ $last: Int
487
+ ) {
488
+ deployments(
489
+ input: $input
490
+ first: $first
491
+ after: $after
492
+ before: $before
493
+ last: $last
494
+ ) {
495
+ edges {
496
+ node {
497
+ id
498
+ status
499
+ createdAt
500
+ updatedAt
501
+ environmentId
502
+ projectId
503
+ serviceId
504
+ url
505
+ staticUrl
506
+ }
507
+ }
508
+ pageInfo {
509
+ hasNextPage
510
+ hasPreviousPage
511
+ startCursor
512
+ endCursor
513
+ }
514
+ }
515
+ }
516
+ `,
517
+ { input, first, after, before, last },
518
+ );
519
+
520
+ return {
521
+ deployments: connectionNodes(data.deployments),
522
+ pageInfo: data.deployments?.pageInfo ?? null,
523
+ };
524
+ };
525
+
526
+ const createProject = async (input = {}) => {
527
+ requireAccountToken("createRailwayProject");
528
+ const data = await request(
529
+ `
530
+ mutation RailwayProjectCreate($input: ProjectCreateInput!) {
531
+ projectCreate(input: $input) {
532
+ id
533
+ name
534
+ description
535
+ workspace {
536
+ id
537
+ name
538
+ }
539
+ }
540
+ }
541
+ `,
542
+ { input: compactObject(input) },
543
+ );
544
+ return data.projectCreate;
545
+ };
546
+
547
+ const updateProject = async ({ projectId, ...input } = {}) => {
548
+ requireAccountToken("updateRailwayProject");
549
+ const id = await requireResolvedProjectId(projectId, "updateRailwayProject");
550
+ const data = await request(
551
+ `
552
+ mutation RailwayProjectUpdate($id: String!, $input: ProjectUpdateInput!) {
553
+ projectUpdate(id: $id, input: $input) {
554
+ id
555
+ name
556
+ description
557
+ prDeploys
558
+ focusedPrEnvironments
559
+ botPrEnvironments
560
+ isPublic
561
+ }
562
+ }
563
+ `,
564
+ { id, input: compactObject(input) },
565
+ );
566
+ return data.projectUpdate;
567
+ };
568
+
569
+ const deleteProject = async (projectId) => {
570
+ requireAccountToken("deleteRailwayProject");
571
+ const id = await requireResolvedProjectId(projectId, "deleteRailwayProject");
572
+ const deleted = await request(
573
+ `
574
+ mutation RailwayProjectDelete($id: String!) {
575
+ projectDelete(id: $id)
576
+ }
577
+ `,
578
+ { id },
579
+ );
580
+ return {
581
+ deleted: Boolean(deleted.projectDelete),
582
+ projectId: id,
583
+ };
584
+ };
585
+
586
+ const transferProject = async ({ projectId, workspaceId } = {}) => {
587
+ requireAccountToken("transferRailwayProject");
588
+ const id = await requireResolvedProjectId(projectId, "transferRailwayProject");
589
+ const transferred = await request(
590
+ `
591
+ mutation RailwayProjectTransfer($projectId: String!, $input: ProjectTransferInput!) {
592
+ projectTransfer(projectId: $projectId, input: $input)
593
+ }
594
+ `,
595
+ {
596
+ projectId: id,
597
+ input: { workspaceId },
598
+ },
599
+ );
600
+ return {
601
+ transferred: Boolean(transferred.projectTransfer),
602
+ projectId: id,
603
+ workspaceId,
604
+ };
605
+ };
606
+
607
+ const createService = async (input = {}) => {
608
+ const data = await request(
609
+ `
610
+ mutation RailwayServiceCreate($input: ServiceCreateInput!) {
611
+ serviceCreate(input: $input) {
612
+ id
613
+ name
614
+ icon
615
+ projectId
616
+ }
617
+ }
618
+ `,
619
+ { input: compactObject(input) },
620
+ );
621
+ return data.serviceCreate;
622
+ };
623
+
624
+ const updateService = async ({ serviceId, ...input } = {}) => {
625
+ const data = await request(
626
+ `
627
+ mutation RailwayServiceUpdate($id: String!, $input: ServiceUpdateInput!) {
628
+ serviceUpdate(id: $id, input: $input) {
629
+ id
630
+ name
631
+ icon
632
+ projectId
633
+ }
634
+ }
635
+ `,
636
+ { id: serviceId, input: compactObject(input) },
637
+ );
638
+ return data.serviceUpdate;
639
+ };
640
+
641
+ const connectService = async ({ serviceId, ...input } = {}) => {
642
+ const data = await request(
643
+ `
644
+ mutation RailwayServiceConnect($id: String!, $input: ServiceConnectInput!) {
645
+ serviceConnect(id: $id, input: $input) {
646
+ id
647
+ name
648
+ icon
649
+ projectId
650
+ }
651
+ }
652
+ `,
653
+ { id: serviceId, input: compactObject(input) },
654
+ );
655
+ return data.serviceConnect;
656
+ };
657
+
658
+ const disconnectService = async (serviceId) => {
659
+ const disconnected = await request(
660
+ `
661
+ mutation RailwayServiceDisconnect($id: String!) {
662
+ serviceDisconnect(id: $id)
663
+ }
664
+ `,
665
+ { id: serviceId },
666
+ );
667
+ return {
668
+ disconnected: Boolean(disconnected.serviceDisconnect),
669
+ serviceId,
670
+ };
671
+ };
672
+
673
+ const deleteService = async ({ serviceId, environmentId } = {}) => {
674
+ const deleted = await request(
675
+ `
676
+ mutation RailwayServiceDelete($id: String!, $environmentId: String) {
677
+ serviceDelete(id: $id, environmentId: $environmentId)
678
+ }
679
+ `,
680
+ { id: serviceId, environmentId },
681
+ );
682
+ return {
683
+ deleted: Boolean(deleted.serviceDelete),
684
+ serviceId,
685
+ environmentId: environmentId ?? null,
686
+ };
687
+ };
688
+
689
+ const updateServiceInstance = async ({
690
+ serviceId,
691
+ environmentId,
692
+ ...input
693
+ } = {}) => {
694
+ const updated = await request(
695
+ `
696
+ mutation RailwayServiceInstanceUpdate(
697
+ $serviceId: String!
698
+ $environmentId: String
699
+ $input: ServiceInstanceUpdateInput!
700
+ ) {
701
+ serviceInstanceUpdate(
702
+ serviceId: $serviceId
703
+ environmentId: $environmentId
704
+ input: $input
705
+ )
706
+ }
707
+ `,
708
+ {
709
+ serviceId,
710
+ environmentId,
711
+ input: compactObject(input),
712
+ },
713
+ );
714
+ return {
715
+ updated: Boolean(updated.serviceInstanceUpdate),
716
+ serviceId,
717
+ environmentId: environmentId ?? null,
718
+ };
719
+ };
720
+
721
+ const deployService = async ({
722
+ serviceId,
723
+ environmentId,
724
+ commitSha,
725
+ latestCommit,
726
+ } = {}) => {
727
+ const deployment = await request(
728
+ `
729
+ mutation RailwayServiceInstanceDeploy(
730
+ $serviceId: String!
731
+ $environmentId: String!
732
+ $commitSha: String
733
+ $latestCommit: Boolean
734
+ ) {
735
+ serviceInstanceDeploy(
736
+ serviceId: $serviceId
737
+ environmentId: $environmentId
738
+ commitSha: $commitSha
739
+ latestCommit: $latestCommit
740
+ ) {
741
+ id
742
+ status
743
+ environmentId
744
+ serviceId
745
+ url
746
+ staticUrl
747
+ }
748
+ }
749
+ `,
750
+ { serviceId, environmentId, commitSha, latestCommit },
751
+ );
752
+ return deployment.serviceInstanceDeploy;
753
+ };
754
+
755
+ const redeployService = async ({ serviceId, environmentId } = {}) => {
756
+ const deployment = await request(
757
+ `
758
+ mutation RailwayServiceInstanceRedeploy(
759
+ $serviceId: String!
760
+ $environmentId: String!
761
+ ) {
762
+ serviceInstanceRedeploy(
763
+ serviceId: $serviceId
764
+ environmentId: $environmentId
765
+ ) {
766
+ id
767
+ status
768
+ environmentId
769
+ serviceId
770
+ url
771
+ staticUrl
772
+ }
773
+ }
774
+ `,
775
+ { serviceId, environmentId },
776
+ );
777
+ return deployment.serviceInstanceRedeploy;
778
+ };
779
+
780
+ const updateServiceInstanceLimits = async ({
781
+ serviceId,
782
+ environmentId,
783
+ memoryGB,
784
+ vCPUs,
785
+ } = {}) => {
786
+ const updated = await request(
787
+ `
788
+ mutation RailwayServiceInstanceLimitsUpdate(
789
+ $input: ServiceInstanceLimitsUpdateInput!
790
+ ) {
791
+ serviceInstanceLimitsUpdate(input: $input)
792
+ }
793
+ `,
794
+ {
795
+ input: compactObject({
796
+ serviceId,
797
+ environmentId,
798
+ memoryGB,
799
+ vCPUs,
800
+ }),
801
+ },
802
+ );
803
+ return {
804
+ updated: Boolean(updated.serviceInstanceLimitsUpdate),
805
+ serviceId,
806
+ environmentId,
807
+ memoryGB: memoryGB ?? null,
808
+ vCPUs: vCPUs ?? null,
809
+ };
810
+ };
811
+
812
+ const createEnvironment = async (input = {}) => {
813
+ const environment = await request(
814
+ `
815
+ mutation RailwayEnvironmentCreate($input: EnvironmentCreateInput!) {
816
+ environmentCreate(input: $input) {
817
+ id
818
+ name
819
+ isEphemeral
820
+ projectId
821
+ }
822
+ }
823
+ `,
824
+ { input: compactObject(input) },
825
+ );
826
+ return environment.environmentCreate;
827
+ };
828
+
829
+ const deleteEnvironment = async (environmentId) => {
830
+ const deleted = await request(
831
+ `
832
+ mutation RailwayEnvironmentDelete($id: String!) {
833
+ environmentDelete(id: $id)
834
+ }
835
+ `,
836
+ { id: environmentId },
837
+ );
838
+ return {
839
+ deleted: Boolean(deleted.environmentDelete),
840
+ environmentId,
841
+ };
842
+ };
843
+
844
+ const upsertVariable = async (input = {}) => {
845
+ const updated = await request(
846
+ `
847
+ mutation RailwayVariableUpsert($input: VariableUpsertInput!) {
848
+ variableUpsert(input: $input)
849
+ }
850
+ `,
851
+ { input: compactObject(input) },
852
+ );
853
+ return {
854
+ updated: Boolean(updated.variableUpsert),
855
+ name: input.name ?? null,
856
+ environmentId: input.environmentId ?? null,
857
+ serviceId: input.serviceId ?? null,
858
+ projectId: input.projectId ?? null,
859
+ };
860
+ };
861
+
862
+ const deleteVariable = async (input = {}) => {
863
+ const deleted = await request(
864
+ `
865
+ mutation RailwayVariableDelete($input: VariableDeleteInput!) {
866
+ variableDelete(input: $input)
867
+ }
868
+ `,
869
+ { input: compactObject(input) },
870
+ );
871
+ return {
872
+ deleted: Boolean(deleted.variableDelete),
873
+ name: input.name ?? null,
874
+ environmentId: input.environmentId ?? null,
875
+ serviceId: input.serviceId ?? null,
876
+ projectId: input.projectId ?? null,
877
+ };
878
+ };
879
+
880
+ const createServiceDomain = async (input = {}) => {
881
+ const domain = await request(
882
+ `
883
+ mutation RailwayServiceDomainCreate($input: ServiceDomainCreateInput!) {
884
+ serviceDomainCreate(input: $input) {
885
+ id
886
+ domain
887
+ }
888
+ }
889
+ `,
890
+ { input: compactObject(input) },
891
+ );
892
+ return domain.serviceDomainCreate;
893
+ };
894
+
895
+ const updateServiceDomain = async (input = {}) => {
896
+ const updated = await request(
897
+ `
898
+ mutation RailwayServiceDomainUpdate($input: ServiceDomainUpdateInput!) {
899
+ serviceDomainUpdate(input: $input)
900
+ }
901
+ `,
902
+ { input: compactObject(input) },
903
+ );
904
+ return {
905
+ updated: Boolean(updated.serviceDomainUpdate),
906
+ serviceDomainId: input.serviceDomainId ?? null,
907
+ };
908
+ };
909
+
910
+ const deleteServiceDomain = async (serviceDomainId) => {
911
+ const deleted = await request(
912
+ `
913
+ mutation RailwayServiceDomainDelete($id: String!) {
914
+ serviceDomainDelete(id: $id)
915
+ }
916
+ `,
917
+ { id: serviceDomainId },
918
+ );
919
+ return {
920
+ deleted: Boolean(deleted.serviceDomainDelete),
921
+ serviceDomainId,
922
+ };
923
+ };
924
+
925
+ const createCustomDomain = async (input = {}) => {
926
+ const domain = await request(
927
+ `
928
+ mutation RailwayCustomDomainCreate($input: CustomDomainCreateInput!) {
929
+ customDomainCreate(input: $input) {
930
+ id
931
+ domain
932
+ }
933
+ }
934
+ `,
935
+ { input: compactObject(input) },
936
+ );
937
+ return domain.customDomainCreate;
938
+ };
939
+
940
+ const updateCustomDomain = async ({
941
+ customDomainId,
942
+ environmentId,
943
+ targetPort,
944
+ } = {}) => {
945
+ const updated = await request(
946
+ `
947
+ mutation RailwayCustomDomainUpdate(
948
+ $id: String!
949
+ $environmentId: String!
950
+ $targetPort: Int
951
+ ) {
952
+ customDomainUpdate(
953
+ id: $id
954
+ environmentId: $environmentId
955
+ targetPort: $targetPort
956
+ )
957
+ }
958
+ `,
959
+ { id: customDomainId, environmentId, targetPort },
960
+ );
961
+ return {
962
+ updated: Boolean(updated.customDomainUpdate),
963
+ customDomainId,
964
+ };
965
+ };
966
+
967
+ const deleteCustomDomain = async (customDomainId) => {
968
+ const deleted = await request(
969
+ `
970
+ mutation RailwayCustomDomainDelete($id: String!) {
971
+ customDomainDelete(id: $id)
972
+ }
973
+ `,
974
+ { id: customDomainId },
975
+ );
976
+ return {
977
+ deleted: Boolean(deleted.customDomainDelete),
978
+ customDomainId,
979
+ };
980
+ };
981
+
982
+ const createVolume = async (input = {}) => {
983
+ const volume = await request(
984
+ `
985
+ mutation RailwayVolumeCreate($input: VolumeCreateInput!) {
986
+ volumeCreate(input: $input) {
987
+ id
988
+ }
989
+ }
990
+ `,
991
+ { input: compactObject(input) },
992
+ );
993
+ return volume.volumeCreate;
994
+ };
995
+
996
+ const deleteVolume = async (volumeId) => {
997
+ const deleted = await request(
998
+ `
999
+ mutation RailwayVolumeDelete($volumeId: String!) {
1000
+ volumeDelete(volumeId: $volumeId)
1001
+ }
1002
+ `,
1003
+ { volumeId },
1004
+ );
1005
+ return {
1006
+ deleted: Boolean(deleted.volumeDelete),
1007
+ volumeId,
1008
+ };
1009
+ };
1010
+
297
1011
  const doctorProject = async ({ projectId } = {}) => {
298
1012
  const project = await getProject(projectId);
299
1013
  const primaryEnvironmentId =
@@ -348,11 +1062,43 @@ export const createRailwayClient = ({
348
1062
  endpoint,
349
1063
  getAuthStatus: () => getRailwayAuthStatus(env),
350
1064
  getCurrentViewer,
1065
+ validateAccountToken,
351
1066
  listProjects,
1067
+ createProject,
1068
+ updateProject,
1069
+ deleteProject,
1070
+ transferProject,
1071
+ getProjectMembers,
352
1072
  getProjectTokenContext,
353
1073
  getProject,
354
1074
  listEnvironments,
355
1075
  getEnvironment,
1076
+ createEnvironment,
1077
+ deleteEnvironment,
1078
+ getService,
1079
+ getServiceInstance,
1080
+ getServiceInstanceLimits,
1081
+ createService,
1082
+ updateService,
1083
+ connectService,
1084
+ disconnectService,
1085
+ deleteService,
1086
+ updateServiceInstance,
1087
+ deployService,
1088
+ redeployService,
1089
+ updateServiceInstanceLimits,
1090
+ getDeployment,
1091
+ listDeployments,
1092
+ upsertVariable,
1093
+ deleteVariable,
1094
+ createServiceDomain,
1095
+ updateServiceDomain,
1096
+ deleteServiceDomain,
1097
+ createCustomDomain,
1098
+ updateCustomDomain,
1099
+ deleteCustomDomain,
1100
+ createVolume,
1101
+ deleteVolume,
356
1102
  doctorProject,
357
1103
  };
358
1104
  };
@@ -367,6 +1113,11 @@ const connectionNodes = (connection) => {
367
1113
  return nodes;
368
1114
  };
369
1115
 
1116
+ const compactObject = (value) =>
1117
+ Object.fromEntries(
1118
+ Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined),
1119
+ );
1120
+
370
1121
  const hasErrors = (payload) =>
371
1122
  typeof payload === "object" &&
372
1123
  payload !== null &&