@proca/cli 3.4.5 → 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 (132) hide show
  1. package/README.md +1991 -61
  2. package/package.json +88 -72
  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/bin/proca-cli +0 -4
  64. package/dist/browser.d.ts +0 -1
  65. package/dist/browser.js +0 -28
  66. package/dist/browser.js.map +0 -1
  67. package/dist/campaign.d.ts +0 -156
  68. package/dist/campaign.js +0 -192
  69. package/dist/campaign.js.map +0 -1
  70. package/dist/cli.d.ts +0 -40
  71. package/dist/cli.js +0 -416
  72. package/dist/cli.js.map +0 -1
  73. package/dist/client.d.ts +0 -2
  74. package/dist/client.js +0 -18
  75. package/dist/client.js.map +0 -1
  76. package/dist/config.d.ts +0 -35
  77. package/dist/config.js +0 -71
  78. package/dist/config.js.map +0 -1
  79. package/dist/crypto.d.ts +0 -40
  80. package/dist/crypto.js +0 -53
  81. package/dist/crypto.js.map +0 -1
  82. package/dist/export.d.ts +0 -15
  83. package/dist/export.js +0 -145
  84. package/dist/export.js.map +0 -1
  85. package/dist/format.d.ts +0 -37
  86. package/dist/format.js +0 -200
  87. package/dist/format.js.map +0 -1
  88. package/dist/index.d.ts +0 -8
  89. package/dist/index.js +0 -39
  90. package/dist/index.js.map +0 -1
  91. package/dist/keys.d.ts +0 -4
  92. package/dist/keys.js +0 -38
  93. package/dist/keys.js.map +0 -1
  94. package/dist/org.d.ts +0 -11
  95. package/dist/org.js +0 -97
  96. package/dist/org.js.map +0 -1
  97. package/dist/proca.d.ts +0 -2095
  98. package/dist/proca.js +0 -398
  99. package/dist/proca.js.map +0 -1
  100. package/dist/queue.d.ts +0 -8
  101. package/dist/queue.js +0 -126
  102. package/dist/queue.js.map +0 -1
  103. package/dist/queueMessage.d.ts +0 -102
  104. package/dist/queueMessage.js +0 -109
  105. package/dist/queueMessage.js.map +0 -1
  106. package/dist/service/actionnetwork.d.ts +0 -10
  107. package/dist/service/actionnetwork.js +0 -302
  108. package/dist/service/actionnetwork.js.map +0 -1
  109. package/dist/service/distance.d.ts +0 -3
  110. package/dist/service/distance.js +0 -96
  111. package/dist/service/distance.js.map +0 -1
  112. package/dist/service/echo.d.ts +0 -4
  113. package/dist/service/echo.js +0 -19
  114. package/dist/service/echo.js.map +0 -1
  115. package/dist/service/email.d.ts +0 -14
  116. package/dist/service/email.js +0 -67
  117. package/dist/service/email.js.map +0 -1
  118. package/dist/service/identity.d.ts +0 -58
  119. package/dist/service/identity.js +0 -190
  120. package/dist/service/identity.js.map +0 -1
  121. package/dist/service/index.d.ts +0 -14
  122. package/dist/service/index.js +0 -61
  123. package/dist/service/index.js.map +0 -1
  124. package/dist/setup.d.ts +0 -2
  125. package/dist/setup.js +0 -291
  126. package/dist/setup.js.map +0 -1
  127. package/dist/util.d.ts +0 -5
  128. package/dist/util.js +0 -30
  129. package/dist/util.js.map +0 -1
  130. package/dist/watch.d.ts +0 -9
  131. package/dist/watch.js +0 -80
  132. package/dist/watch.js.map +0 -1
@@ -0,0 +1,132 @@
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
+ FragmentMtt,
6
+ FragmentOrg,
7
+ FragmentStats,
8
+ FragmentSummary,
9
+ } from "#src/queries/campaign.mjs";
10
+ import { gql, query } from "#src/urql.mjs";
11
+
12
+ export const getCampaign = (params) => {
13
+ const d = new CampaignGet([]);
14
+ return d.fetch(params);
15
+ };
16
+
17
+ export default class CampaignGet extends Command {
18
+ actionTypes = new Set();
19
+
20
+ static args = this.multiid();
21
+
22
+ static description = "view a campaign";
23
+
24
+ static examples = ["<%= config.bin %> <%= command.id %> -i 42"];
25
+
26
+ static flags = {
27
+ // flag with no value (-f, --force)
28
+ ...this.flagify({ multiid: true }),
29
+ config: Flags.boolean({
30
+ description: "display the config",
31
+ default: false,
32
+ allowNo: true,
33
+ }),
34
+ stats: Flags.boolean({
35
+ description: "display the stats",
36
+ default: true,
37
+ allowNo: true,
38
+ }),
39
+ locale: Flags.string({
40
+ description: "display a locale",
41
+ }),
42
+ };
43
+
44
+ fetch = async ({ id, name }) => {
45
+ const GetCampaignDocument = gql`
46
+ query GetCampaign($id: Int, $name: String, $withStats: Boolean = false) {
47
+ campaign (name: $name, id: $id) {
48
+ ...Summary
49
+ ...Org
50
+ config
51
+ ...Stats @include(if: $withStats)
52
+ ...Mtt
53
+ }
54
+ }
55
+ ${FragmentStats}
56
+ ${FragmentSummary}
57
+ ${FragmentOrg}
58
+ ${FragmentMtt}
59
+ `;
60
+ const result = await query(GetCampaignDocument, {
61
+ id: id,
62
+ name: name,
63
+ withStats: this.flags.stats,
64
+ });
65
+ return result.campaign;
66
+ };
67
+
68
+ simplify = (d) => {
69
+ const result = {
70
+ id: d.id,
71
+ Name: d.name,
72
+ Title: d.title,
73
+ Org: d.org.name,
74
+ Status: d.status,
75
+ locales: d.config.locales && Object.keys(d.config.locales).join(" "),
76
+ journey: d.config.journey?.join(" → "),
77
+ };
78
+ if (d.mtt) {
79
+ // we have an mtt
80
+ const hhmm = (date) =>
81
+ new Date(date).toLocaleTimeString(undefined, {
82
+ hour: "2-digit",
83
+ minute: "2-digit",
84
+ hour12: false,
85
+ });
86
+ result.from = d.mtt.startAt.substring(0, 10);
87
+ result.to = d.mtt.endAt.substring(0, 10);
88
+ result.period = `${hhmm(d.mtt.startAt)}↔${hhmm(d.mtt.endAt)}`;
89
+ result["test email"] = d.mtt.testEmail;
90
+ result["mtt template"] = d.mtt.template;
91
+ result["cc contacts"] = d.mtt.ccContacts?.join(", ");
92
+ result["cc sender"] = d.mtt.ccSender;
93
+ result["drip delivery"] = d.mtt.dripDelivery;
94
+ }
95
+ if (this.flags.stats) {
96
+ result["#Supporters"] = d.stats.supporterCount;
97
+
98
+ this.actionTypes.forEach((type) => {
99
+ const action = d.stats.actionCount.find(
100
+ (action) => action.actionType === type,
101
+ );
102
+ if (action) result[`#${type}`] = action.count;
103
+ });
104
+ }
105
+ return result;
106
+ };
107
+
108
+ table = (r) => {
109
+ r.config = JSON.parse(r.config);
110
+ super.table(r, null, null);
111
+ if (this.flags.locale) {
112
+ this.prettyJson(r.config?.locales[this.flags.locale]);
113
+ }
114
+ if (this.flags.config) {
115
+ r.config.locales = undefined;
116
+ this.prettyJson(r.config);
117
+ }
118
+ };
119
+
120
+ async run() {
121
+ const { args, flags } = await this.parse();
122
+
123
+ const data = await this.fetch({ id: flags.id, name: flags.name });
124
+ if (this.flags.stats) {
125
+ data.stats.actionCount.forEach((d) => {
126
+ //skip share_confirmed?
127
+ this.actionTypes.add(d.actionType);
128
+ });
129
+ }
130
+ return this.output(data);
131
+ }
132
+ }
@@ -0,0 +1,161 @@
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 const getCampaignList = (params) => {
12
+ const d = new CampaignList([]);
13
+ return d.fetch(params);
14
+ };
15
+
16
+ export default class CampaignList extends Command {
17
+ actionTypes = new Set();
18
+
19
+ static args = {
20
+ title: Args.string({ description: "name of the campaign, % for wildchar" }),
21
+ };
22
+
23
+ static description = "list all the campaigns";
24
+
25
+ static examples = ["<%= config.bin %> <%= command.id %> %pizza%"];
26
+
27
+ static flags = {
28
+ // flag with no value (-f, --force)
29
+ ...super.globalFlags,
30
+ org: Flags.string({
31
+ char: "o",
32
+ description: "campaigns of the organisation (coordinator or partner)",
33
+ exactlyOne: ["org", "title"],
34
+ helpValue: "<organisation name>",
35
+ }),
36
+ title: Flags.string({
37
+ char: "t",
38
+ description: "name of the campaign, % for wildchar",
39
+ helpValue: "<campaign title>",
40
+ }),
41
+ stats: Flags.boolean({
42
+ description: "display the stats",
43
+ default: true,
44
+ allowNo: true,
45
+ }),
46
+ };
47
+
48
+ OrgSearch = async (name) => {
49
+ const SearchCampaignsDocument = gql`
50
+ query SearchCampaigns($org: String!, $withStats: Boolean = false) {
51
+ org (name:$org) {
52
+ campaigns {
53
+ ...Summary
54
+ ...Org
55
+ ...Stats @include(if: $withStats)
56
+ }
57
+ }
58
+ }
59
+ ${FragmentStats}
60
+ ${FragmentOrg}
61
+ ${FragmentSummary}
62
+ `;
63
+ const result = await query(SearchCampaignsDocument, {
64
+ org: name,
65
+ withStats: this.flags.stats,
66
+ });
67
+ return result.org.campaigns;
68
+ //return result.campaigns.map (d => {d.config = JSON.parse(d.config); return d});
69
+ };
70
+
71
+ Search = async (title) => {
72
+ const SearchCampaignsDocument = gql`
73
+ query SearchCampaigns($title: String!, $withStats: Boolean = false) {
74
+ campaigns(title: $title) {
75
+ ...Summary
76
+ ...Org
77
+ ...Stats @include(if: $withStats)
78
+ }
79
+ }
80
+ ${FragmentStats}
81
+ ${FragmentOrg}
82
+ ${FragmentSummary}
83
+ `;
84
+ const result = await query(SearchCampaignsDocument, {
85
+ title: title,
86
+ withStats: this.flags.stats,
87
+ });
88
+ return result.campaigns;
89
+ //return result.campaigns.map (d => {d.config = JSON.parse(d.config); return d});
90
+ };
91
+
92
+ simplify = (d) => {
93
+ const result = {
94
+ id: d.id,
95
+ Name: d.name,
96
+ Title: d.title,
97
+ Org: d.org.name,
98
+ Status: d.status,
99
+ };
100
+ if (this.flags.stats) {
101
+ result["#Supporters"] = d.stats.supporterCount;
102
+
103
+ this.actionTypes.forEach((type) => {
104
+ const action = d.stats.actionCount.find(
105
+ (action) => action.actionType === type,
106
+ );
107
+ if (action) result[`#${type}`] = action.count;
108
+ });
109
+ }
110
+ return result;
111
+ };
112
+
113
+ table = (r) => {
114
+ super.table(r, null, (table) => table.sort(["id|des"]).toString());
115
+ };
116
+
117
+ async run() {
118
+ const { args, flags } = await this.parse(CampaignList);
119
+ let data = [];
120
+
121
+ if (args.title && flags.title) {
122
+ throw new Error(
123
+ `${this.id} EITHER [title of the campaign] OR --title [title of the campaign]`,
124
+ );
125
+ }
126
+ if (args.title) {
127
+ flags.title = args.title;
128
+ }
129
+
130
+ if (!flags.title && !flags.org) {
131
+ throw new Error(
132
+ `${this.id} -t [title of the campaign] or -o [organisation]`,
133
+ );
134
+ }
135
+
136
+ if (flags.title) {
137
+ data = await this.Search(flags.title);
138
+ if (this.flags.stats) {
139
+ data.forEach((d) => {
140
+ d.stats.actionCount.forEach((d) => {
141
+ //skip share_confirmed?
142
+ this.actionTypes.add(d.actionType);
143
+ });
144
+ });
145
+ }
146
+ }
147
+
148
+ if (flags.org) {
149
+ data = await this.OrgSearch(flags.org);
150
+ if (this.flags.stats) {
151
+ data.forEach((d) => {
152
+ d.stats.actionCount.forEach((d) => {
153
+ //skip share_confirmed?
154
+ this.actionTypes.add(d.actionType);
155
+ });
156
+ });
157
+ }
158
+ }
159
+ return this.output(data);
160
+ }
161
+ }
@@ -0,0 +1,131 @@
1
+ import { Flags } from "@oclif/core";
2
+ import Command from "#src/procaCommand.mjs";
3
+ import { FragmentMtt } from "#src/queries/campaign.mjs";
4
+ import { gql, mutation } from "#src/urql.mjs";
5
+
6
+ export default class CampaignMtt extends Command {
7
+ static args = this.multiid();
8
+
9
+ static description = "set the mail to target (mtt) params";
10
+
11
+ static examples = [
12
+ "<%= config.bin %> <%= command.id %> -n <test-mtt-campaign>",
13
+ ];
14
+
15
+ static flags = {
16
+ ...this.flagify({ multiid: true }),
17
+ from: Flags.string({
18
+ description: "start date (yyyy-mm-dd)",
19
+ required: false,
20
+ }),
21
+ to: Flags.string({
22
+ description: "end date (yyyy-mm-dd)",
23
+ required: false,
24
+ }),
25
+ template: Flags.string({
26
+ description: "mtt template to use",
27
+ }),
28
+ period: Flags.string({
29
+ description: "period of the day (HH:HH-HH:HH)",
30
+ default: "09:09-18:18",
31
+ }),
32
+ email: Flags.string({
33
+ description: "test email address",
34
+ }),
35
+ cc: Flags.string({
36
+ description: "comma-separated list of CC email addresses",
37
+ }),
38
+ sender: Flags.boolean({
39
+ description: "add sender to CC",
40
+ default: false,
41
+ }),
42
+ drip: Flags.boolean({
43
+ description: "drip delivery or deliver as fast as possible",
44
+ default: false,
45
+ }),
46
+ };
47
+
48
+ updateMtt = async (flags) => {
49
+ const Query = gql`
50
+ mutation (
51
+ $id: Int,
52
+ $name: String
53
+ $mtt: CampaignMttInput!
54
+ ) {
55
+ updateCampaign (id:$id, name: $name, input: { mtt: $mtt }) {
56
+ id, name
57
+ ...Mtt
58
+ }
59
+ ${FragmentMtt}
60
+ }
61
+ `;
62
+
63
+ const testEmail = flags.email || `campaign+${flags.name}@proca.app`;
64
+
65
+ const [startPeriod, endPeriod] = flags.period.split("-");
66
+ const [startHour, startMinute] = startPeriod.split(":");
67
+ const [endHour, endMinute] = endPeriod.split(":");
68
+
69
+ const mtt = {
70
+ testEmail,
71
+ };
72
+
73
+ if (flags.template) mtt.messageTemplate = flags.template;
74
+ if (flags.email) mtt.testEmail = testEmail;
75
+ if (flags.cc) mtt.ccContacts = flags.cc.split(",").map((e) => e.trim());
76
+ if (flags.sender) mtt.ccSender = flags.sender;
77
+
78
+ if (flags.from) {
79
+ const startAt = new Date(flags.from);
80
+ startAt.setHours(startHour, startMinute, 0, 0);
81
+ mtt.startAt = startAt.toISOString();
82
+ }
83
+
84
+ if (flags.to) {
85
+ const endAt = new Date(flags.to);
86
+ endAt.setHours(endHour, endMinute, 0, 0);
87
+ mtt.endAt = endAt.toISOString();
88
+ }
89
+
90
+ const result = await mutation(Query, {
91
+ id: flags.id,
92
+ name: flags.name,
93
+ mtt,
94
+ });
95
+ return result.updateCampaign;
96
+ };
97
+
98
+ simplify = (d) => {
99
+ const result = { id: d.id, Name: d.name };
100
+ const hhmm = (date) =>
101
+ new Date(date).toLocaleTimeString(undefined, {
102
+ hour: "2-digit",
103
+ minute: "2-digit",
104
+ hour12: false,
105
+ });
106
+ if (d.mtt.startAt && d.mtt.endAt) {
107
+ result.from = d.mtt.startAt.substring(0, 10);
108
+ result.to = d.mtt.endAt.substring(0, 10);
109
+ result.period = `${hhmm(d.mtt.startAt)}↔${hhmm(d.mtt.endAt)}`;
110
+ }
111
+ result["test email"] = d.mtt.testEmail;
112
+ result["mtt template"] = d.mtt.messageTemplate;
113
+ if (d.mtt.ccContacts?.length)
114
+ result["cc contacts"] = d.mtt.ccContacts.join(", ");
115
+ if (typeof d.mtt.ccSender !== "undefined") {
116
+ result["cc sender"] = d.mtt.ccSender ? "yes" : "no";
117
+ }
118
+
119
+ return result;
120
+ };
121
+
122
+ table = (r) => {
123
+ super.table(r, null, null);
124
+ };
125
+
126
+ async run() {
127
+ const { args, flags } = await this.parse();
128
+ const result = await this.updateMtt(flags);
129
+ this.output(result);
130
+ }
131
+ }
@@ -0,0 +1,19 @@
1
+ query SearchCampaigns($name: String!) {
2
+ campaigns(name: $name) {
3
+ id
4
+ name
5
+ title
6
+ config
7
+ }
8
+ }
9
+
10
+ query GetOrgCampaigns($org: String!) {
11
+ org(name: $org) {
12
+ campaigns {
13
+ id
14
+ name
15
+ title
16
+ config
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,63 @@
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 } from "#src/urql.mjs";
5
+
6
+ export default class CampaignStatus extends Command {
7
+ static args = this.multiid();
8
+ static aliases = ["campaign:close"];
9
+
10
+ static examples = [
11
+ "<%= config.bin %> <%= command.id %> -name <campaign>",
12
+ "<%= config.bin %> <%= command.id %> -i <campaign_id>",
13
+ ];
14
+
15
+ static isCloseCommand =
16
+ process.argv.includes("close") ||
17
+ this.commandPath?.includes("close") ||
18
+ this.id?.includes("close");
19
+
20
+ static flags = {
21
+ status: Flags.string({
22
+ ...this.flagify({ multiid: true }),
23
+ description: "Status to set",
24
+ required: true,
25
+ default: this.isCloseCommand ? "close" : undefined,
26
+ options: ["draft", "live", "closed", "ignored"],
27
+ }),
28
+ };
29
+
30
+ updateStatus = async (props) => {
31
+ const Query = gql`
32
+ mutation (
33
+ $id: Int,
34
+ $name: String
35
+ $status: String!
36
+ ) {
37
+ updateCampaign (id:$id, input: { name: $name,status: $status }) {
38
+ name
39
+ org {name}
40
+ status
41
+ title
42
+ }
43
+ }
44
+ `;
45
+
46
+ const result = await mutation(Query, {
47
+ // org: props.org,
48
+ id: props.id,
49
+ name: props.name,
50
+ status: props.status.toUpperCase(),
51
+ });
52
+
53
+ console.log("result", result);
54
+ return result.updateCampaign;
55
+ };
56
+
57
+ async run() {
58
+ const { args, flags } = await this.parse();
59
+
60
+ const data = await this.updateStatus(flags);
61
+ return this.output(data);
62
+ }
63
+ }
@@ -0,0 +1,124 @@
1
+ import { Flags } from "@oclif/core";
2
+ import prompts from "prompts";
3
+ import CampaignGet from "#src/commands/campaign/get.mjs";
4
+ import WidgetList from "#src/commands/widget/list.mjs";
5
+ import WidgetUpdate from "#src/commands/widget/update.mjs";
6
+ import Command from "#src/procaCommand.mjs";
7
+
8
+ export default class CampaignWidgetArchive extends Command {
9
+ static args = this.multiid(); // Add this!
10
+
11
+ static description = "Archive all widgets in the campaign by adding suffix";
12
+
13
+ static examples = [
14
+ "<%= config.bin %> <%= command.id %> old_campaign",
15
+ "<%= config.bin %> <%= command.id %> -n old_campaign --suffix _backup",
16
+ "<%= config.bin %> <%= command.id %> old_campaign --dry-run",
17
+ ];
18
+
19
+ static flags = {
20
+ ...this.flagify({ multiid: true }),
21
+ suffix: Flags.string({
22
+ char: "s",
23
+ description: "custom suffix to append (default: _archive)",
24
+ helpValue: "<suffix>",
25
+ default: "_archive",
26
+ }),
27
+ "dry-run": Flags.boolean({
28
+ description: "preview changes without executing",
29
+ default: false,
30
+ }),
31
+ };
32
+
33
+ fetchCampaign = async ({ id, name }) => {
34
+ const campaignGet = new CampaignGet([], this.config);
35
+ return await campaignGet.fetch({ id, name });
36
+ };
37
+
38
+ fetchWidgets = async (campaignName) => {
39
+ const widgetList = new WidgetList([], this.config);
40
+ widgetList.flags = { campaign: campaignName, config: true };
41
+ return await widgetList.fetchCampaign(campaignName);
42
+ };
43
+
44
+ async run() {
45
+ const { flags } = await this.parse();
46
+ const { id, name, suffix, "dry-run": dryRun } = flags;
47
+
48
+ this.log(`Fetching source campaign: ${name || id}`);
49
+ const campaign = await this.fetchCampaign({ id, name });
50
+
51
+ if (!campaign) {
52
+ this.error("Campaign not found");
53
+ return;
54
+ }
55
+
56
+ this.log(`Fetching widgets for campaign: ${campaign.name}`);
57
+ const widgets = await this.fetchWidgets(campaign.name);
58
+
59
+ if (!widgets || widgets.length === 0) {
60
+ this.warn("No widgets found for this campaign");
61
+ return;
62
+ }
63
+
64
+ this.log(`Found ${widgets.length} widgets`);
65
+
66
+ const renameList = widgets.map((widget) => ({
67
+ id: widget.id,
68
+ oldName: widget.name,
69
+ newName: `${widget.name}${suffix}`,
70
+ locale: widget.locale,
71
+ }));
72
+
73
+ this.log("\nArchive plan:");
74
+ this.table(renameList);
75
+
76
+ if (dryRun) {
77
+ this.log("\n[DRY RUN] No changes made");
78
+ return renameList;
79
+ }
80
+
81
+ // Confirm before proceeding
82
+ const response = await prompts({
83
+ type: "confirm",
84
+ name: "proceed",
85
+ message: `Archive ${renameList.length} widgets by adding suffix "${suffix}"?`,
86
+ initial: false,
87
+ });
88
+
89
+ if (!response.proceed) {
90
+ this.log("Cancelled");
91
+ return;
92
+ }
93
+
94
+ const widgetUpdate = new WidgetUpdate([], this.config);
95
+ const results = [];
96
+
97
+ for (const item of renameList) {
98
+ try {
99
+ this.log(`Archiving: ${item.oldName} → ${item.newName}`);
100
+ const input = {
101
+ name: item.newName,
102
+ locale: item.locale,
103
+ };
104
+ const result = await widgetUpdate.update(item.id, input);
105
+ results.push({
106
+ id: result.id,
107
+ name: result.name,
108
+ status: "success",
109
+ });
110
+ } catch (error) {
111
+ this.error(`Failed to archive ${item.oldName}: ${error.message}`);
112
+ results.push({
113
+ id: item.id,
114
+ name: item.oldName,
115
+ status: "failed",
116
+ error: error.message,
117
+ });
118
+ }
119
+ }
120
+
121
+ this.log("\nArchive complete!");
122
+ return this.output(results);
123
+ }
124
+ }