@proca/cli 3.3.1 → 3.8.3

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.
Files changed (145) hide show
  1. package/README.md +1992 -60
  2. package/package.json +88 -71
  3. package/proca-cli +8 -0
  4. package/src/commands/action/add.mjs +192 -0
  5. package/src/commands/action/confirm.mjs +93 -0
  6. package/src/commands/action/count.mjs +61 -0
  7. package/src/commands/action/list.mjs +204 -0
  8. package/src/commands/action/replay.mjs +56 -0
  9. package/src/commands/action/requeue.mjs +156 -0
  10. package/src/commands/campaign/add.mjs +113 -0
  11. package/src/commands/campaign/copy.mjs +91 -0
  12. package/src/commands/campaign/delete.mjs +41 -0
  13. package/src/commands/campaign/get.mjs +132 -0
  14. package/src/commands/campaign/list.mjs +161 -0
  15. package/src/commands/campaign/mtt.mjs +131 -0
  16. package/src/commands/campaign/queries.graphql +19 -0
  17. package/src/commands/campaign/status.mjs +63 -0
  18. package/src/commands/campaign/widget/archive.mjs +124 -0
  19. package/src/commands/campaign/widget/copy.mjs +175 -0
  20. package/src/commands/campaign/widget/get.mjs +19 -0
  21. package/src/commands/campaign/widget/index.mjs +17 -0
  22. package/src/commands/campaign/widget/rebuild.mjs +50 -0
  23. package/src/commands/config/add.mjs +97 -0
  24. package/src/commands/config/folder.mjs +42 -0
  25. package/src/commands/config/server.mjs +33 -0
  26. package/src/commands/config/set.mjs +103 -0
  27. package/src/commands/config/user.mjs +79 -0
  28. package/src/commands/contact/count.mjs +41 -0
  29. package/src/commands/contact/list.mjs +207 -0
  30. package/src/commands/org/add.mjs +75 -0
  31. package/src/commands/org/crm.mjs +88 -0
  32. package/src/commands/org/delete.mjs +48 -0
  33. package/src/commands/org/email.mjs +111 -0
  34. package/src/commands/org/get.mjs +152 -0
  35. package/src/commands/service/add.mjs +78 -0
  36. package/src/commands/service/list.mjs +24 -0
  37. package/src/commands/target/add.mjs +94 -0
  38. package/src/commands/template/add.mjs +97 -0
  39. package/src/commands/template/list.mjs +61 -0
  40. package/src/commands/user/get.mjs +91 -0
  41. package/src/commands/user/invite.mjs +56 -0
  42. package/src/commands/user/join.mjs +81 -0
  43. package/src/commands/user/leave.mjs +57 -0
  44. package/src/commands/user/list.mjs +80 -0
  45. package/src/commands/user/reset.mjs +83 -0
  46. package/src/commands/widget/add.mjs +116 -0
  47. package/src/commands/widget/delete.mjs +45 -0
  48. package/src/commands/widget/get.mjs +60 -0
  49. package/src/commands/widget/list.mjs +135 -0
  50. package/src/commands/widget/rebuild.mjs +64 -0
  51. package/src/commands/widget/update.mjs +174 -0
  52. package/src/config.mjs +49 -0
  53. package/src/generated/schema.json +10677 -0
  54. package/src/hooks/help.mjs +14 -0
  55. package/src/hooks/init.mjs +41 -0
  56. package/src/index.mjs +1 -0
  57. package/src/procaCommand.mjs +316 -0
  58. package/src/queries/campaign.mjs +35 -0
  59. package/src/queries/widget.mjs +25 -0
  60. package/src/urql.mjs +60 -0
  61. package/src/util/twitter.mjs +23 -0
  62. package/theme.json +29 -0
  63. package/LICENSE +0 -661
  64. package/bin/proca-cli +0 -4
  65. package/dist/a.d.ts +0 -1
  66. package/dist/a.js +0 -21
  67. package/dist/a.js.map +0 -1
  68. package/dist/browser.d.ts +0 -1
  69. package/dist/browser.js +0 -24
  70. package/dist/browser.js.map +0 -1
  71. package/dist/campaign.d.ts +0 -156
  72. package/dist/campaign.js +0 -188
  73. package/dist/campaign.js.map +0 -1
  74. package/dist/cli.d.ts +0 -40
  75. package/dist/cli.js +0 -411
  76. package/dist/cli.js.map +0 -1
  77. package/dist/client.d.ts +0 -2
  78. package/dist/client.js +0 -18
  79. package/dist/client.js.map +0 -1
  80. package/dist/config.d.ts +0 -35
  81. package/dist/config.js +0 -71
  82. package/dist/config.js.map +0 -1
  83. package/dist/crypto.d.ts +0 -40
  84. package/dist/crypto.js +0 -53
  85. package/dist/crypto.js.map +0 -1
  86. package/dist/decrypt.d.ts +0 -7
  87. package/dist/decrypt.js +0 -29
  88. package/dist/decrypt.js.map +0 -1
  89. package/dist/export.d.ts +0 -15
  90. package/dist/export.js +0 -141
  91. package/dist/export.js.map +0 -1
  92. package/dist/format.d.ts +0 -37
  93. package/dist/format.js +0 -200
  94. package/dist/format.js.map +0 -1
  95. package/dist/index.d.ts +0 -8
  96. package/dist/index.js +0 -35
  97. package/dist/index.js.map +0 -1
  98. package/dist/keys.d.ts +0 -4
  99. package/dist/keys.js +0 -38
  100. package/dist/keys.js.map +0 -1
  101. package/dist/org.d.ts +0 -11
  102. package/dist/org.js +0 -93
  103. package/dist/org.js.map +0 -1
  104. package/dist/proca.d.ts +0 -2066
  105. package/dist/proca.js +0 -396
  106. package/dist/proca.js.map +0 -1
  107. package/dist/queue.d.ts +0 -8
  108. package/dist/queue.js +0 -126
  109. package/dist/queue.js.map +0 -1
  110. package/dist/queueMessage.d.ts +0 -102
  111. package/dist/queueMessage.js +0 -109
  112. package/dist/queueMessage.js.map +0 -1
  113. package/dist/scalarLocations.d.ts +0 -4
  114. package/dist/scalarLocations.js +0 -236
  115. package/dist/scalarLocations.js.map +0 -1
  116. package/dist/service/actionnetwork.d.ts +0 -10
  117. package/dist/service/actionnetwork.js +0 -302
  118. package/dist/service/actionnetwork.js.map +0 -1
  119. package/dist/service/distance.d.ts +0 -3
  120. package/dist/service/distance.js +0 -92
  121. package/dist/service/distance.js.map +0 -1
  122. package/dist/service/echo.d.ts +0 -4
  123. package/dist/service/echo.js +0 -19
  124. package/dist/service/echo.js.map +0 -1
  125. package/dist/service/email.d.ts +0 -14
  126. package/dist/service/email.js +0 -67
  127. package/dist/service/email.js.map +0 -1
  128. package/dist/service/identity.d.ts +0 -58
  129. package/dist/service/identity.js +0 -190
  130. package/dist/service/identity.js.map +0 -1
  131. package/dist/service/index.d.ts +0 -14
  132. package/dist/service/index.js +0 -61
  133. package/dist/service/index.js.map +0 -1
  134. package/dist/setup.d.ts +0 -2
  135. package/dist/setup.js +0 -287
  136. package/dist/setup.js.map +0 -1
  137. package/dist/test.d.ts +0 -1
  138. package/dist/test.js +0 -33
  139. package/dist/test.js.map +0 -1
  140. package/dist/util.d.ts +0 -5
  141. package/dist/util.js +0 -30
  142. package/dist/util.js.map +0 -1
  143. package/dist/watch.d.ts +0 -9
  144. package/dist/watch.js +0 -76
  145. package/dist/watch.js.map +0 -1
@@ -0,0 +1,204 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import { error, stdout, ux } from "@oclif/core/ux";
3
+ import Command from "#src/procaCommand.mjs";
4
+ import {
5
+ FragmentOrg,
6
+ FragmentStats,
7
+ FragmentSummary,
8
+ } from "#src/queries/campaign.mjs";
9
+ import { gql, query } from "#src/urql.mjs";
10
+
11
+ export default class List extends Command {
12
+ actionTypes = new Set();
13
+
14
+ static args = {
15
+ title: Args.string({ description: "name of the campaign, % for wildchar" }),
16
+ };
17
+
18
+ static examples = ["<%= config.bin %> <%= command.id %> %pizza%"];
19
+
20
+ static flags = {
21
+ // flag with no value (-f, --force)
22
+ ...super.globalFlags,
23
+ org: Flags.string({
24
+ char: "o",
25
+ description: "campaigns of the organisation (coordinator or partner)",
26
+ required: true,
27
+ // exactlyOne: ["org", "title"],
28
+ helpValue: "<organisation name>",
29
+ }),
30
+ campaign: Flags.string({
31
+ char: "c",
32
+ description: "name of the campaign, % for wildchar",
33
+ helpValue: "<campaign name>",
34
+ }),
35
+ limit: Flags.string({
36
+ description: "max number of actions",
37
+ parse: (input) => Number.parseInt(input, 10),
38
+ }),
39
+ after: Flags.string({
40
+ description: "only actions after a date",
41
+ helpValue: "2025-04-09",
42
+ parse: (input) => new Date(input).toISOString(),
43
+ }),
44
+ today: Flags.boolean({
45
+ description: "only actions today",
46
+ exclusive: ["after"],
47
+ parse: (input) => `${new Date().toISOString().split("T")[0]}T00:00:00Z`,
48
+ }),
49
+ optin: Flags.boolean({
50
+ description: "only export the optin actions",
51
+ default: false,
52
+ }),
53
+ testing: Flags.boolean({
54
+ description: "also export the test actions",
55
+ default: false,
56
+ }),
57
+ doi: Flags.boolean({
58
+ description: "only export the double optin actions",
59
+ default: false,
60
+ }),
61
+ utm: Flags.boolean({
62
+ description: "display the utm tracking parameters",
63
+ default: true,
64
+ allowNo: true,
65
+ exclusive: ["simplify"],
66
+ }),
67
+ comment: Flags.boolean({
68
+ description: "display the comment",
69
+ default: true,
70
+ allowNo: true,
71
+ exclusive: ["simplify"],
72
+ }),
73
+ };
74
+
75
+ fetch = async (flags) => {
76
+ const Document = gql`
77
+ query (
78
+ $after: DateTime
79
+ $campaignId: Int
80
+ $campaignName: String
81
+ $includeTesting: Boolean
82
+ $limit: Int
83
+ $onlyDoubleOptIn: Boolean
84
+ $onlyOptIn: Boolean
85
+ $orgName: String!
86
+ $start: Int
87
+ ) {
88
+ actions (
89
+ after: $after
90
+ campaignId: $campaignId
91
+ campaignName: $campaignName
92
+ includeTesting: $includeTesting
93
+ limit: $limit
94
+ onlyDoubleOptIn: $onlyDoubleOptIn
95
+ onlyOptIn: $onlyOptIn
96
+ orgName: $orgName
97
+ start: $start
98
+ ) {
99
+ actionId
100
+ actionPage {
101
+ locale
102
+ name
103
+ }
104
+ actionType
105
+ campaign {
106
+ name
107
+ }
108
+ contact {
109
+ contactRef
110
+ payload
111
+ nonce
112
+ publicKey {
113
+ public
114
+ }
115
+ }
116
+ createdAt
117
+ customFields
118
+ privacy {
119
+ emailStatus
120
+ emailStatusChanged
121
+ givenAt
122
+ optIn
123
+ withConsent
124
+ }
125
+ tracking {
126
+ campaign
127
+ content
128
+ medium
129
+ source
130
+ }
131
+ }
132
+ }
133
+ `;
134
+ const result = await query(Document, {
135
+ after: flags.after,
136
+ // "campaignId": 42,
137
+ campaignName: flags.campaign,
138
+ includeTesting: flags.testing,
139
+ limit: flags.limit,
140
+ onlyDoubleOptIn: flags.doi,
141
+ onlyOptIn: flags.optin,
142
+ orgName: flags.org,
143
+ start: flags.start,
144
+ });
145
+ return result.actions.map((d) => {
146
+ d.customFields = JSON.parse(d.customFields);
147
+ if (!d.contact.publicKey) {
148
+ const ref = d.contactRef;
149
+ d.contact = JSON.parse(d.contact.payload);
150
+ d.contact.contactRef = ref;
151
+ } else {
152
+ this.error(
153
+ `encrypted contact we need the private key for ${d.contact.publicKey.public}`,
154
+ );
155
+ }
156
+ return d;
157
+ });
158
+ // return result.exportActions;
159
+ };
160
+
161
+ simplify = (d) => {
162
+ const result = {
163
+ id: d.actionId,
164
+ firstname: d.contact.firstName,
165
+ country: d.contact.country,
166
+ email: d.contact.email,
167
+ type: d.actionType,
168
+ date: d.createdAt,
169
+ campaign: d.campaign.name,
170
+ widget_id: d.actionPage.id,
171
+ widget: d.actionPage.name,
172
+ // customFields
173
+ };
174
+ if (this.flags.comment && d.customFields?.comment)
175
+ result.comment = d.customFields.comment;
176
+ if (d.customFields?.emailProvider)
177
+ result.provider = d.customFields.emailProvider;
178
+ if (this.flags.utm && d.tracking) {
179
+ result.utm_medium =
180
+ d.tracking.medium === "unknown" ? undefined : d.tracking.medium;
181
+ result.utm_source =
182
+ d.tracking.source === "unknown" ? undefined : d.tracking.source;
183
+ result.utm_campaign =
184
+ d.tracking.campaign === "unknown" ? undefined : d.tracking.campaign;
185
+ if (d.tracking.content)
186
+ result.utm_content =
187
+ d.tracking.content === "unknown" ? undefined : d.tracking.content;
188
+ }
189
+ return result;
190
+ };
191
+
192
+ _table = (r) => {
193
+ super.table(r, null, (table) => table.sort(["id|des"]).toString());
194
+ };
195
+
196
+ async run() {
197
+ const { args, flags } = await this.parse();
198
+ if (flags.today) flags.after = flags.today;
199
+ let data = [];
200
+
201
+ data = await this.fetch(flags);
202
+ return this.output(data);
203
+ }
204
+ }
@@ -0,0 +1,56 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import { error, stdout, ux } from "@oclif/core/ux";
3
+ import Command from "#src/procaCommand.mjs";
4
+ import { gql, query } from "#src/urql.mjs";
5
+
6
+ export default class ReplayAction extends Command {
7
+ static examples = ["<%= config.bin %> <%= command.id %> %pizza%"];
8
+
9
+ static flags = {
10
+ // flag with no value (-f, --force)
11
+ ...super.globalFlags,
12
+ org: Flags.string({
13
+ char: "o",
14
+ description: "campaigns of the organisation (coordinator or partner)",
15
+ required: true,
16
+ // exactlyOne: ["org", "title"],
17
+ helpValue: "<organisation name>",
18
+ }),
19
+ /* queue: Flag.string({
20
+ default: "CUSTOM_ACTION_DELIVER",
21
+ options: [
22
+ 'CUSTOM_ACTION_CONFIRM',
23
+ 'CUSTOM_ACTION_DELIVER',
24
+ 'CUSTOM_SUPPORTER_CONFIRM',
25
+ 'EMAIL_SUPPORTER',
26
+ 'SQS',
27
+ 'WEBHOOK'
28
+ ],
29
+ }),
30
+ */
31
+ campaign: Flags.string({
32
+ char: "c",
33
+ description: "name of the campaign, % for wildchar",
34
+ helpValue: "<campaign title>",
35
+ }),
36
+ };
37
+
38
+ mutate = async (org, id, queue) => {
39
+ const Document = gql`
40
+ mutation Requeue($org: String!, $ids: [Int!]!, $queue: Queue!){
41
+ requeueActions(orgName:$org, ids: $ids, queue: $queue) {
42
+ count failed
43
+ }
44
+ }`;
45
+ };
46
+
47
+ async run() {
48
+ const { args, flags } = await this.parse();
49
+ let data = [];
50
+ const ids = [];
51
+ this.fatal("not implemented yet");
52
+ process.exit(1);
53
+ data = await this.mutate(flags.org, ids, flags.queue);
54
+ return this.output(data);
55
+ }
56
+ }
@@ -0,0 +1,156 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import { error, stdout, ux } from "@oclif/core/ux";
3
+ import Command from "#src/procaCommand.mjs";
4
+ import { gql, mutation, query } from "#src/urql.mjs";
5
+
6
+ export default class ActionRequeue extends Command {
7
+ static description = "requeue actions";
8
+
9
+ static examples = ["<%= config.bin %> <%= command.id %>"];
10
+
11
+ static flags = {
12
+ ...super.globalFlags,
13
+ org: Flags.string({
14
+ char: "o",
15
+ required: true,
16
+ description: "name of the org",
17
+ helpValue: "<org name>",
18
+ }),
19
+ campaign: Flags.string({
20
+ char: "c",
21
+ description: "name of the campaign, % for wildchar",
22
+ helpValue: "<campaign name>",
23
+ }),
24
+ queue: Flags.string({
25
+ char: "q",
26
+ required: true,
27
+ description: "queue to redeliver to",
28
+ default: "CUSTOM_ACTION_DELIVER",
29
+ options: [
30
+ "CUSTOM_ACTION_CONFIRM",
31
+ "CUSTOM_ACTION_DELIVER",
32
+ "CUSTOM_SUPPORTER_CONFIRM",
33
+ "EMAIL_SUPPORTER",
34
+ "SQS",
35
+ "WEBHOOK",
36
+ ],
37
+ }),
38
+ limit: Flags.string({
39
+ description: "how many actions per page",
40
+ default: 1000,
41
+ parse: (input) => Number.parseInt(input, 10),
42
+ }),
43
+ after: Flags.string({
44
+ description: "only actions after a date",
45
+ helpValue: "2025-04-09",
46
+ parse: (input) => new Date(input).toISOString(),
47
+ }),
48
+ today: Flags.boolean({
49
+ description: "only actions today",
50
+ exclusive: ["after"],
51
+ parse: (input) => `${new Date().toISOString().split("T")[0]}T00:00:00Z`,
52
+ }),
53
+ optin: Flags.boolean({
54
+ description: "only export the optin actions",
55
+ default: false,
56
+ }),
57
+ testing: Flags.boolean({
58
+ description: "also export the test actions",
59
+ default: false,
60
+ }),
61
+ doi: Flags.boolean({
62
+ description: "only export the double optin actions",
63
+ default: false,
64
+ }),
65
+ };
66
+
67
+ process = async (flags) => {
68
+ let page = 0;
69
+ let actions = 0;
70
+ let requeued = 0;
71
+ let error = 0;
72
+ while (true) {
73
+ const ids = await this.fetch(flags);
74
+ if (ids.length === 0) break;
75
+ const result = await this.requeue(flags.org, flags.queue, ids);
76
+ requeued += result.count;
77
+ error += result.failed;
78
+ flags.start = ids[ids.length - 1] + 1;
79
+ process.stdout.write(`\rrequeued ${requeued}, id> ${flags.start}`);
80
+ page += 1;
81
+ actions += ids.length;
82
+ }
83
+ process.stdout.write("\r\x1b[K");
84
+ return { pages: page, actions, requeued, error };
85
+ };
86
+
87
+ fetch = async (flags) => {
88
+ const Document = gql`
89
+ query (
90
+ $after: DateTime
91
+ $campaignId: Int
92
+ $campaignName: String
93
+ $includeTesting: Boolean
94
+ $limit: Int
95
+ $onlyDoubleOptIn: Boolean
96
+ $onlyOptIn: Boolean
97
+ $orgName: String!
98
+ $start: Int
99
+ ) {
100
+ actions (
101
+ after: $after
102
+ campaignId: $campaignId
103
+ campaignName: $campaignName
104
+ includeTesting: $includeTesting
105
+ limit: $limit
106
+ onlyDoubleOptIn: $onlyDoubleOptIn
107
+ onlyOptIn: $onlyOptIn
108
+ orgName: $orgName
109
+ start: $start
110
+ ) {
111
+ actionId
112
+ }
113
+ }
114
+ `;
115
+ const result = await query(Document, {
116
+ after: flags.after,
117
+ campaignName: flags.campaign,
118
+ includeTesting: flags.testing,
119
+ limit: flags.limit,
120
+ onlyDoubleOptIn: flags.doi,
121
+ onlyOptIn: flags.optin,
122
+ orgName: flags.org,
123
+ start: flags.start,
124
+ });
125
+ const ids = [];
126
+ result.actions.forEach((d) => ids.push(d.actionId));
127
+ return ids;
128
+ };
129
+
130
+ requeue = async (org, queue, ids) => {
131
+ const Document = gql`
132
+ mutation ($ids: [Int!], $org: String!, $queue: Queue!) {
133
+ requeueActions(ids: $ids, orgName: $org, queue: $queue) {
134
+ count
135
+ failed
136
+ }
137
+ }`;
138
+ const result = await mutation(Document, {
139
+ org,
140
+ queue,
141
+ ids,
142
+ });
143
+ //return result.users.map (d => {d.config = JSON.parse(d.config); return d});
144
+ return result.requeueActions;
145
+ };
146
+
147
+ table = (r) => {
148
+ super.table(r, null, null);
149
+ };
150
+
151
+ async run() {
152
+ const { args, flags } = await this.parse();
153
+ const data = await this.process(flags);
154
+ this.output(data);
155
+ }
156
+ }
@@ -0,0 +1,113 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import { error, stdout, ux } from "@oclif/core/ux";
3
+ import OrgGet from "#src/commands/org/get.mjs";
4
+ import Command from "#src/procaCommand.mjs";
5
+ import { gql, mutation } from "#src/urql.mjs";
6
+ import { getTwitter } from "#src/util/twitter.mjs";
7
+
8
+ export default class CampaignAdd extends Command {
9
+ static args = {
10
+ title: Args.string({
11
+ description: "title of the campaign",
12
+ multiple: true,
13
+ }),
14
+ };
15
+ // static strict = false;
16
+
17
+ static examples = [
18
+ "<%= config.bin %> <%= command.id %> -n <new_campaign> the full name of the campaign",
19
+ ];
20
+
21
+ static flags = {
22
+ // flag with no value (-f, --force)
23
+ ...super.globalFlags,
24
+ name: Flags.string({
25
+ char: "n",
26
+ description: "name of the campaign",
27
+ helpValue: "<campaign name>",
28
+ required: true,
29
+ }),
30
+ org: Flags.string({
31
+ char: "o",
32
+ description: "name of the coordinator",
33
+ helpValue: "<org name>",
34
+ required: true,
35
+ }),
36
+ };
37
+
38
+ create = async (campaign, customConfig = null) => {
39
+ const org = await this.getOrg(campaign.org);
40
+
41
+ let config;
42
+
43
+ // Use custom config if provided, otherwise create default
44
+ if (customConfig) {
45
+ config =
46
+ typeof customConfig === "string"
47
+ ? JSON.parse(customConfig)
48
+ : customConfig;
49
+ } else {
50
+ // Default config creation (existing functionality)
51
+ config = {
52
+ locales: {
53
+ en: {
54
+ "campaign:": {
55
+ description: "",
56
+ },
57
+ "common:": {},
58
+ },
59
+ },
60
+ };
61
+
62
+ if (org.config.locale && org.config.locale !== "en") {
63
+ config.locales[org.config.locale] = {};
64
+ }
65
+
66
+ if (!config.portal) {
67
+ config.portal = [];
68
+ }
69
+ }
70
+
71
+ const AddOrgDocument = gql`
72
+ mutation ($org: String!
73
+ $name: String!
74
+ $title: String!
75
+ $config: Json!
76
+ ) {
77
+ addCampaign (input: { name: $name, title: $title, config: $config }, orgName: $org) {
78
+ name
79
+ title
80
+ config
81
+ }
82
+ }
83
+ `;
84
+
85
+ const result = await mutation(AddOrgDocument, {
86
+ org: org.name,
87
+ name: campaign.name,
88
+ title: campaign.title,
89
+ config: JSON.stringify(config),
90
+ });
91
+
92
+ console.log("result", result);
93
+ return result;
94
+ };
95
+
96
+ async getOrg(orgName) {
97
+ const { config } = this;
98
+ const orgGet = new OrgGet({}, this.config);
99
+ const org = await orgGet.fetch({ name: orgName });
100
+ return org;
101
+ }
102
+
103
+ async run() {
104
+ const { args, flags } = await this.parse();
105
+ const campaign = {
106
+ org: flags.org,
107
+ name: flags.name,
108
+ title: args.title || flags.name,
109
+ };
110
+ const data = await this.create(campaign);
111
+ return this.output(data);
112
+ }
113
+ }
@@ -0,0 +1,91 @@
1
+ import { Flags } from "@oclif/core";
2
+ import CampaignAdd from "#src/commands/campaign/add.mjs";
3
+ import CampaignGet from "#src/commands/campaign/get.mjs";
4
+ import Command from "#src/procaCommand.mjs";
5
+
6
+ export default class CampaignCopy extends Command {
7
+ static args = this.multiid();
8
+
9
+ static description = "Copy campaign settings to a new campaign";
10
+
11
+ static examples = [
12
+ "<%= config.bin %> <%= command.id %> test_2025 --to test_2026",
13
+ "<%= config.bin %> <%= command.id %> -n old_campaign --to new_campaign -o different_org",
14
+ ];
15
+
16
+ static flags = {
17
+ ...this.flagify({ multiid: true }),
18
+ to: Flags.string({
19
+ char: "t",
20
+ required: true,
21
+ description: "new campaign name",
22
+ helpValue: "<campaign name>",
23
+ }),
24
+ org: Flags.string({
25
+ char: "o",
26
+ description:
27
+ "organization for the new campaign (defaults to source campaign org)",
28
+ helpValue: "<org name>",
29
+ }),
30
+ title: Flags.string({
31
+ description:
32
+ "title for the new campaign (defaults to source campaign title)",
33
+ helpValue: "<campaign title>",
34
+ }),
35
+ "dry-run": Flags.boolean({
36
+ description: "preview changes without executing",
37
+ default: false,
38
+ }),
39
+ };
40
+
41
+ fetchCampaign = async ({ id, name }) => {
42
+ const campaignGet = new CampaignGet([], this.config);
43
+ return await campaignGet.fetch({ id, name });
44
+ };
45
+
46
+ async run() {
47
+ const { flags } = await this.parse();
48
+ const { id, name, to, org, title, "dry-run": dryRun } = flags;
49
+
50
+ this.log(`Fetching source campaign: ${name || id}`);
51
+ const sourceCampaign = await this.fetchCampaign({ id, name });
52
+
53
+ const newCampaign = {
54
+ name: to,
55
+ title: title || sourceCampaign.title,
56
+ org: org || sourceCampaign.org.name,
57
+ };
58
+
59
+ this.log("\n=== CAMPAIGN COPY ===");
60
+ this.log(`Source: ${sourceCampaign.name}`);
61
+ this.log("\nNew Campaign:");
62
+ this.log(` Name: ${newCampaign.name}`);
63
+ this.log(` Title: ${newCampaign.title}`);
64
+ this.log(` Org: ${newCampaign.org}`);
65
+
66
+ if (dryRun) {
67
+ this.log("\n[DRY RUN] No changes made");
68
+ return newCampaign;
69
+ }
70
+
71
+ this.log(`\nCreating campaign: ${to}`);
72
+ try {
73
+ const campaignAdd = new CampaignAdd([], this.config);
74
+
75
+ const result = await campaignAdd.create(
76
+ {
77
+ org: newCampaign.org,
78
+ name: newCampaign.name,
79
+ title: newCampaign.title,
80
+ },
81
+ sourceCampaign.config,
82
+ );
83
+
84
+ this.log(`✓ Campaign created: ${result.addCampaign.name}`);
85
+ return this.output(result.addCampaign);
86
+ } catch (error) {
87
+ this.error(`Failed to create campaign: ${error.message}`);
88
+ return;
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,41 @@
1
+ import { Flags } from "@oclif/core";
2
+ import Command from "#src/procaCommand.mjs";
3
+ import { gql, mutation } from "#src/urql.mjs";
4
+
5
+ export default class CampaignDelete extends Command {
6
+ static args = this.multiid();
7
+
8
+ static description = "delete a campaign";
9
+ static examples = [
10
+ "<%= config.bin %> <%= command.id %> 42",
11
+ "<%= config.bin %> <%= command.id %> -i 42",
12
+ "<%= config.bin %> <%= command.id %> my_campaign",
13
+ "<%= config.bin %> <%= command.id %> -n my_campaign",
14
+ ];
15
+
16
+ static flags = {
17
+ ...this.flagify({ multiid: true }),
18
+ };
19
+
20
+ mutate = async ({ id, name }) => {
21
+ const Document = gql`
22
+ mutation ($id: Int, $name: String) {
23
+ deleteCampaign(id: $id, name: $name)
24
+ }
25
+ `;
26
+ const result = await mutation(Document, { id, name });
27
+ return result.deleteCampaign;
28
+ };
29
+
30
+ async run() {
31
+ const { flags } = await this.parse();
32
+ const { id, name } = flags;
33
+
34
+ if (!id && !name) {
35
+ this.error("You must specify a campaign id or name");
36
+ }
37
+
38
+ const data = await this.mutate({ id, name });
39
+ return this.output(data);
40
+ }
41
+ }