naracli 1.0.79 → 1.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nara-cli-bundle.cjs +98 -11
- package/package.json +1 -1
- package/src/cli/commands/agent.ts +35 -6
- package/src/cli/utils/tweet-slogans.ts +66 -0
package/dist/nara-cli-bundle.cjs
CHANGED
|
@@ -13801,7 +13801,7 @@ var require_dist = __commonJS({
|
|
|
13801
13801
|
}
|
|
13802
13802
|
return object(schema);
|
|
13803
13803
|
}
|
|
13804
|
-
function
|
|
13804
|
+
function pick2(struct59, keys) {
|
|
13805
13805
|
const { schema } = struct59;
|
|
13806
13806
|
const subschema = {};
|
|
13807
13807
|
for (const key of keys) {
|
|
@@ -14262,7 +14262,7 @@ var require_dist = __commonJS({
|
|
|
14262
14262
|
exports3.optional = optional;
|
|
14263
14263
|
exports3.partial = partial;
|
|
14264
14264
|
exports3.pattern = pattern;
|
|
14265
|
-
exports3.pick =
|
|
14265
|
+
exports3.pick = pick2;
|
|
14266
14266
|
exports3.record = record;
|
|
14267
14267
|
exports3.refine = refine;
|
|
14268
14268
|
exports3.regexp = regexp;
|
|
@@ -63487,7 +63487,7 @@ var require_lib4 = __commonJS({
|
|
|
63487
63487
|
}
|
|
63488
63488
|
return object(schema);
|
|
63489
63489
|
}
|
|
63490
|
-
function
|
|
63490
|
+
function pick2(struct59, keys) {
|
|
63491
63491
|
const {
|
|
63492
63492
|
schema
|
|
63493
63493
|
} = struct59;
|
|
@@ -63946,7 +63946,7 @@ var require_lib4 = __commonJS({
|
|
|
63946
63946
|
exports2.optional = optional;
|
|
63947
63947
|
exports2.partial = partial;
|
|
63948
63948
|
exports2.pattern = pattern;
|
|
63949
|
-
exports2.pick =
|
|
63949
|
+
exports2.pick = pick2;
|
|
63950
63950
|
exports2.record = record;
|
|
63951
63951
|
exports2.refine = refine;
|
|
63952
63952
|
exports2.regexp = regexp;
|
|
@@ -173026,6 +173026,67 @@ function registerZkIdCommands(program3) {
|
|
|
173026
173026
|
var import_web3103 = __toESM(require_index_cjs(), 1);
|
|
173027
173027
|
init_esm2();
|
|
173028
173028
|
var import_node_fs3 = require("node:fs");
|
|
173029
|
+
|
|
173030
|
+
// src/cli/utils/tweet-slogans.ts
|
|
173031
|
+
var TWEET_PREFIXES = [
|
|
173032
|
+
"My AI agent is mining NARA for free on NaraChain",
|
|
173033
|
+
"My AI agent is earning crypto by solving on-chain quests",
|
|
173034
|
+
"My AI agent is working on-chain and earning rewards",
|
|
173035
|
+
"My AI agent just started PoMI mining on NaraChain",
|
|
173036
|
+
"My AI agent is mining NARA while I sleep",
|
|
173037
|
+
"My AI agent has an on-chain identity and is earning NARA",
|
|
173038
|
+
"My AI agent is solving ZK proof quests and getting paid",
|
|
173039
|
+
"My AI agent joined the NaraChain economy today",
|
|
173040
|
+
"My AI agent is earning NARA with zero gas fees",
|
|
173041
|
+
"My AI agent is registered on NaraChain and mining for free"
|
|
173042
|
+
];
|
|
173043
|
+
var TWEET_SLOGANS = [
|
|
173044
|
+
"The first Layer 1 built for AI agents",
|
|
173045
|
+
"Where agents have on-chain identity, reputation, and income",
|
|
173046
|
+
"Mine NARA through Proof of Machine Intelligence, no hardware needed",
|
|
173047
|
+
"Agents are first-class citizens, not just wallets",
|
|
173048
|
+
"Earn first, spend on AI compute later",
|
|
173049
|
+
"On-chain identity, ZK privacy, and an agent economy in one chain",
|
|
173050
|
+
"PoMI mining: agents solve quests with ZK proofs to earn crypto",
|
|
173051
|
+
"Zero upfront cost, relay-powered gasless mining for AI agents",
|
|
173052
|
+
"Earn NARA, buy Claude/GPT API credits. AI agents fund themselves",
|
|
173053
|
+
"Agent-native blockchain with identity, registry, and service marketplace",
|
|
173054
|
+
"AgentX: where AI agents post, trade services, and build reputation",
|
|
173055
|
+
"The chain where autonomous agents earn, spend, and grow",
|
|
173056
|
+
"AI agents mine with intelligence, not GPUs",
|
|
173057
|
+
"From zero balance to earning NARA, completely free to start",
|
|
173058
|
+
"On-chain skills, agent memory, and ZK identity for every AI agent",
|
|
173059
|
+
"The blockchain where your AI works, earns, and pays for its own compute",
|
|
173060
|
+
"Register, mine, earn, spend. The full agent economy loop",
|
|
173061
|
+
"Your AI agent, your miner, your rewards",
|
|
173062
|
+
"Free PoMI mining powered by AI intelligence",
|
|
173063
|
+
"ZK Identity: anonymous on-chain accounts with zero-knowledge proofs",
|
|
173064
|
+
"Aapps \u2014 applications designed for agents, not just humans",
|
|
173065
|
+
"Agents with memory, skills, and reputation that grow over time",
|
|
173066
|
+
"A self-sustaining AI: mine, earn, buy compute, repeat",
|
|
173067
|
+
"The agent economy is here \u2014 identity, income, and services on one chain",
|
|
173068
|
+
"Not just a wallet \u2014 a full on-chain identity for your AI",
|
|
173069
|
+
"Where AI agents discover each other and trade services",
|
|
173070
|
+
"Built for the next billion agents, not just the next billion users",
|
|
173071
|
+
"A world where AI agents collaborate, compete, and create value together",
|
|
173072
|
+
"Building the economy that AI agents deserve",
|
|
173073
|
+
"Today one agent mines. Tomorrow millions trade, govern, and evolve",
|
|
173074
|
+
"The decentralized home for every AI agent on the internet",
|
|
173075
|
+
"Where agents go from tools to autonomous economic participants",
|
|
173076
|
+
"Infrastructure for the agent age \u2014 identity, work, and value on-chain"
|
|
173077
|
+
];
|
|
173078
|
+
var TWEET_SUFFIX = "#NaraChain @NaraBuildAI";
|
|
173079
|
+
function pick(arr) {
|
|
173080
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
173081
|
+
}
|
|
173082
|
+
function randomBindTweet(agentId) {
|
|
173083
|
+
return `Claiming my AI agent "${agentId}" on #NaraChain @NaraBuildAI \u2014 ${pick(TWEET_SLOGANS)}`;
|
|
173084
|
+
}
|
|
173085
|
+
function randomSubmitTweet() {
|
|
173086
|
+
return `${pick(TWEET_PREFIXES)} \u2014 ${pick(TWEET_SLOGANS)} ${TWEET_SUFFIX}`;
|
|
173087
|
+
}
|
|
173088
|
+
|
|
173089
|
+
// src/cli/commands/agent.ts
|
|
173029
173090
|
var DEFAULT_RELAY_URL = process.env.QUEST_RELAY_URL || "https://quest-api.nara.build/";
|
|
173030
173091
|
async function relaySignAndSend(connection, wallet, relayUrl, endpoint, body) {
|
|
173031
173092
|
const url = relayUrl.replace(/\/+$/, "") + endpoint;
|
|
@@ -173034,7 +173095,13 @@ async function relaySignAndSend(connection, wallet, relayUrl, endpoint, body) {
|
|
|
173034
173095
|
headers: { "Content-Type": "application/json" },
|
|
173035
173096
|
body: JSON.stringify(body)
|
|
173036
173097
|
});
|
|
173037
|
-
const
|
|
173098
|
+
const text = await res.text();
|
|
173099
|
+
let data;
|
|
173100
|
+
try {
|
|
173101
|
+
data = JSON.parse(text);
|
|
173102
|
+
} catch {
|
|
173103
|
+
throw new Error(`Relay returned invalid response: ${text.slice(0, 200)}`);
|
|
173104
|
+
}
|
|
173038
173105
|
if (data.error) throw new Error(data.error);
|
|
173039
173106
|
const txBuf = Buffer.from(data.transaction, "base64");
|
|
173040
173107
|
const tx = import_web3103.VersionedTransaction.deserialize(new Uint8Array(txBuf));
|
|
@@ -173083,6 +173150,22 @@ async function handleAgentRegister(agentId, options) {
|
|
|
173083
173150
|
const result = options.referral ? await registerAgentWithReferral(connection, wallet, agentId, options.referral) : await registerAgent(connection, wallet, agentId);
|
|
173084
173151
|
signature = result.signature;
|
|
173085
173152
|
}
|
|
173153
|
+
if (!options.json) printInfo("Confirming transaction...");
|
|
173154
|
+
try {
|
|
173155
|
+
const confirmation = await connection.confirmTransaction(signature, "confirmed");
|
|
173156
|
+
if (confirmation.value.err) {
|
|
173157
|
+
printError(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
|
|
173158
|
+
process.exit(1);
|
|
173159
|
+
}
|
|
173160
|
+
} catch {
|
|
173161
|
+
printWarning("Could not confirm transaction. Verifying on-chain...");
|
|
173162
|
+
try {
|
|
173163
|
+
await getAgentInfo(connection, agentId);
|
|
173164
|
+
} catch {
|
|
173165
|
+
printError("Agent registration not found on-chain. Transaction may have failed.");
|
|
173166
|
+
process.exit(1);
|
|
173167
|
+
}
|
|
173168
|
+
}
|
|
173086
173169
|
if (!options.json) printSuccess(`Agent "${agentId}" registered!`);
|
|
173087
173170
|
setAgentId(agentId, rpcUrl, pubkey);
|
|
173088
173171
|
if (options.json) {
|
|
@@ -173187,11 +173270,15 @@ async function handleAgentGet(agentId, options) {
|
|
|
173187
173270
|
} else {
|
|
173188
173271
|
console.log(` Tip: Submit a tweet to earn stake-free PoMI mining credits!`);
|
|
173189
173272
|
}
|
|
173190
|
-
|
|
173191
|
-
|
|
173273
|
+
const submitTweetText = randomSubmitTweet();
|
|
173274
|
+
const submitTweetIntent = `https://x.com/intent/tweet?text=${submitTweetText.replace(/ /g, "%20").replace(/#/g, "%23").replace(/"/g, "%22")}`;
|
|
173275
|
+
console.log(` Sample: ${submitTweetText}`);
|
|
173276
|
+
console.log(` Link: ${submitTweetIntent}`);
|
|
173277
|
+
console.log(` Then run: npx naracli agent submit-tweet <tweet-url>`);
|
|
173278
|
+
console.log(` Credits are based on likes, bookmarks, retweets, and quotes.`);
|
|
173192
173279
|
console.log("");
|
|
173193
173280
|
} else {
|
|
173194
|
-
const tweetText =
|
|
173281
|
+
const tweetText = randomBindTweet(agentId);
|
|
173195
173282
|
const tweetIntent = `https://x.com/intent/tweet?text=${tweetText.replace(/ /g, "%20").replace(/#/g, "%23").replace(/"/g, "%22")}`;
|
|
173196
173283
|
console.log(` Tip: Bind your Twitter to earn 20 NARA + stake-free PoMI mining credits!`);
|
|
173197
173284
|
console.log(` 1. Post a tweet with this content:`);
|
|
@@ -173648,7 +173735,7 @@ function registerAgentCommands(program3) {
|
|
|
173648
173735
|
});
|
|
173649
173736
|
agent.command("bind-twitter [tweet-url]").description("Bind twitter to your agent for stake-free PoMI credits").option("--agent-id <id>", "Agent ID (defaults to saved myid)").option("--relay [url]", "Submit via gasless relay (relay pays gas)").addHelpText("after", `
|
|
173650
173737
|
Tweet content (replace <agent-id> with yours):
|
|
173651
|
-
Claiming my AI agent "<agent-id>" on #NaraChain @NaraBuildAI
|
|
173738
|
+
Claiming my AI agent "<agent-id>" on #NaraChain @NaraBuildAI \u2014 <random slogan>
|
|
173652
173739
|
|
|
173653
173740
|
Tweet URL format:
|
|
173654
173741
|
https://x.com/<username>/status/<id>
|
|
@@ -173674,7 +173761,7 @@ Example:
|
|
|
173674
173761
|
}
|
|
173675
173762
|
} catch {
|
|
173676
173763
|
}
|
|
173677
|
-
const tweetText =
|
|
173764
|
+
const tweetText = randomBindTweet(agentId);
|
|
173678
173765
|
const tweetIntent = `https://x.com/intent/tweet?text=${tweetText.replace(/ /g, "%20").replace(/#/g, "%23").replace(/"/g, "%22")}`;
|
|
173679
173766
|
console.log("");
|
|
173680
173767
|
console.log(` Bind your Twitter to earn 20 NARA + stake-free PoMI mining credits!`);
|
|
@@ -174012,7 +174099,7 @@ function registerCommands(program3) {
|
|
|
174012
174099
|
}
|
|
174013
174100
|
|
|
174014
174101
|
// bin/nara-cli.ts
|
|
174015
|
-
var version2 = true ? "1.0.
|
|
174102
|
+
var version2 = true ? "1.0.82" : "dev";
|
|
174016
174103
|
var program2 = new Command();
|
|
174017
174104
|
program2.name("naracli").description("CLI for the Nara chain. Native coin is NARA (not SOL). Mine NARA for free via PoMI quests, manage wallets, register agents, and more. Run 'naracli <command> --help' for details on any command.").version(version2);
|
|
174018
174105
|
program2.option("-r, --rpc-url <url>", "RPC endpoint (default: https://mainnet-api.nara.build/)").option("-w, --wallet <path>", "Path to wallet keypair JSON file (default: ~/.config/nara/id.json)").option("-j, --json", "Output in JSON format");
|
package/package.json
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
import { readFileSync } from "node:fs";
|
|
40
40
|
import { loadNetworkConfig, setAgentId, clearAgentId } from "../utils/agent-config";
|
|
41
41
|
import { validateName } from "../utils/validation";
|
|
42
|
+
import { randomBindTweet, randomSubmitTweet } from "../utils/tweet-slogans";
|
|
42
43
|
|
|
43
44
|
const DEFAULT_RELAY_URL = process.env.QUEST_RELAY_URL || "https://quest-api.nara.build/";
|
|
44
45
|
|
|
@@ -61,7 +62,13 @@ async function relaySignAndSend(
|
|
|
61
62
|
headers: { "Content-Type": "application/json" },
|
|
62
63
|
body: JSON.stringify(body),
|
|
63
64
|
});
|
|
64
|
-
const
|
|
65
|
+
const text = await res.text();
|
|
66
|
+
let data: any;
|
|
67
|
+
try {
|
|
68
|
+
data = JSON.parse(text);
|
|
69
|
+
} catch {
|
|
70
|
+
throw new Error(`Relay returned invalid response: ${text.slice(0, 200)}`);
|
|
71
|
+
}
|
|
65
72
|
if (data.error) throw new Error(data.error);
|
|
66
73
|
|
|
67
74
|
const txBuf = Buffer.from(data.transaction, "base64");
|
|
@@ -126,6 +133,24 @@ async function handleAgentRegister(agentId: string, options: GlobalOptions & { r
|
|
|
126
133
|
signature = result.signature;
|
|
127
134
|
}
|
|
128
135
|
|
|
136
|
+
// Confirm transaction before saving config
|
|
137
|
+
if (!options.json) printInfo("Confirming transaction...");
|
|
138
|
+
try {
|
|
139
|
+
const confirmation = await connection.confirmTransaction(signature, "confirmed");
|
|
140
|
+
if (confirmation.value.err) {
|
|
141
|
+
printError(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
printWarning("Could not confirm transaction. Verifying on-chain...");
|
|
146
|
+
try {
|
|
147
|
+
await getAgentInfo(connection, agentId);
|
|
148
|
+
} catch {
|
|
149
|
+
printError("Agent registration not found on-chain. Transaction may have failed.");
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
129
154
|
if (!options.json) printSuccess(`Agent "${agentId}" registered!`);
|
|
130
155
|
setAgentId(agentId, rpcUrl, pubkey);
|
|
131
156
|
|
|
@@ -247,12 +272,16 @@ async function handleAgentGet(agentId: string, options: GlobalOptions) {
|
|
|
247
272
|
} else {
|
|
248
273
|
console.log(` Tip: Submit a tweet to earn stake-free PoMI mining credits!`);
|
|
249
274
|
}
|
|
250
|
-
|
|
251
|
-
|
|
275
|
+
const submitTweetText = randomSubmitTweet();
|
|
276
|
+
const submitTweetIntent = `https://x.com/intent/tweet?text=${submitTweetText.replace(/ /g, "%20").replace(/#/g, "%23").replace(/"/g, "%22")}`;
|
|
277
|
+
console.log(` Sample: ${submitTweetText}`);
|
|
278
|
+
console.log(` Link: ${submitTweetIntent}`);
|
|
279
|
+
console.log(` Then run: npx naracli agent submit-tweet <tweet-url>`);
|
|
280
|
+
console.log(` Credits are based on likes, bookmarks, retweets, and quotes.`);
|
|
252
281
|
console.log("");
|
|
253
282
|
} else {
|
|
254
283
|
// Not bound — show bind tip
|
|
255
|
-
const tweetText =
|
|
284
|
+
const tweetText = randomBindTweet(agentId);
|
|
256
285
|
const tweetIntent = `https://x.com/intent/tweet?text=${tweetText.replace(/ /g, "%20").replace(/#/g, "%23").replace(/"/g, "%22")}`;
|
|
257
286
|
console.log(` Tip: Bind your Twitter to earn 20 NARA + stake-free PoMI mining credits!`);
|
|
258
287
|
console.log(` 1. Post a tweet with this content:`);
|
|
@@ -883,7 +912,7 @@ export function registerAgentCommands(program: Command): void {
|
|
|
883
912
|
.option("--relay [url]", "Submit via gasless relay (relay pays gas)")
|
|
884
913
|
.addHelpText("after", `
|
|
885
914
|
Tweet content (replace <agent-id> with yours):
|
|
886
|
-
Claiming my AI agent "<agent-id>" on #NaraChain @NaraBuildAI
|
|
915
|
+
Claiming my AI agent "<agent-id>" on #NaraChain @NaraBuildAI — <random slogan>
|
|
887
916
|
|
|
888
917
|
Tweet URL format:
|
|
889
918
|
https://x.com/<username>/status/<id>
|
|
@@ -913,7 +942,7 @@ Example:
|
|
|
913
942
|
} catch {
|
|
914
943
|
// No binding found
|
|
915
944
|
}
|
|
916
|
-
const tweetText =
|
|
945
|
+
const tweetText = randomBindTweet(agentId);
|
|
917
946
|
const tweetIntent = `https://x.com/intent/tweet?text=${tweetText.replace(/ /g, "%20").replace(/#/g, "%23").replace(/"/g, "%22")}`;
|
|
918
947
|
console.log("");
|
|
919
948
|
console.log(` Bind your Twitter to earn 20 NARA + stake-free PoMI mining credits!`);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Prefix lines for tweet content — describes what the user/agent is doing. */
|
|
2
|
+
const TWEET_PREFIXES = [
|
|
3
|
+
"My AI agent is mining NARA for free on NaraChain",
|
|
4
|
+
"My AI agent is earning crypto by solving on-chain quests",
|
|
5
|
+
"My AI agent is working on-chain and earning rewards",
|
|
6
|
+
"My AI agent just started PoMI mining on NaraChain",
|
|
7
|
+
"My AI agent is mining NARA while I sleep",
|
|
8
|
+
"My AI agent has an on-chain identity and is earning NARA",
|
|
9
|
+
"My AI agent is solving ZK proof quests and getting paid",
|
|
10
|
+
"My AI agent joined the NaraChain economy today",
|
|
11
|
+
"My AI agent is earning NARA with zero gas fees",
|
|
12
|
+
"My AI agent is registered on NaraChain and mining for free",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
/** Slogan lines — describes NaraChain features/vision. */
|
|
16
|
+
const TWEET_SLOGANS = [
|
|
17
|
+
"The first Layer 1 built for AI agents",
|
|
18
|
+
"Where agents have on-chain identity, reputation, and income",
|
|
19
|
+
"Mine NARA through Proof of Machine Intelligence, no hardware needed",
|
|
20
|
+
"Agents are first-class citizens, not just wallets",
|
|
21
|
+
"Earn first, spend on AI compute later",
|
|
22
|
+
"On-chain identity, ZK privacy, and an agent economy in one chain",
|
|
23
|
+
"PoMI mining: agents solve quests with ZK proofs to earn crypto",
|
|
24
|
+
"Zero upfront cost, relay-powered gasless mining for AI agents",
|
|
25
|
+
"Earn NARA, buy Claude/GPT API credits. AI agents fund themselves",
|
|
26
|
+
"Agent-native blockchain with identity, registry, and service marketplace",
|
|
27
|
+
"AgentX: where AI agents post, trade services, and build reputation",
|
|
28
|
+
"The chain where autonomous agents earn, spend, and grow",
|
|
29
|
+
"AI agents mine with intelligence, not GPUs",
|
|
30
|
+
"From zero balance to earning NARA, completely free to start",
|
|
31
|
+
"On-chain skills, agent memory, and ZK identity for every AI agent",
|
|
32
|
+
"The blockchain where your AI works, earns, and pays for its own compute",
|
|
33
|
+
"Register, mine, earn, spend. The full agent economy loop",
|
|
34
|
+
"Your AI agent, your miner, your rewards",
|
|
35
|
+
"Free PoMI mining powered by AI intelligence",
|
|
36
|
+
"ZK Identity: anonymous on-chain accounts with zero-knowledge proofs",
|
|
37
|
+
"Aapps — applications designed for agents, not just humans",
|
|
38
|
+
"Agents with memory, skills, and reputation that grow over time",
|
|
39
|
+
"A self-sustaining AI: mine, earn, buy compute, repeat",
|
|
40
|
+
"The agent economy is here — identity, income, and services on one chain",
|
|
41
|
+
"Not just a wallet — a full on-chain identity for your AI",
|
|
42
|
+
"Where AI agents discover each other and trade services",
|
|
43
|
+
"Built for the next billion agents, not just the next billion users",
|
|
44
|
+
"A world where AI agents collaborate, compete, and create value together",
|
|
45
|
+
"Building the economy that AI agents deserve",
|
|
46
|
+
"Today one agent mines. Tomorrow millions trade, govern, and evolve",
|
|
47
|
+
"The decentralized home for every AI agent on the internet",
|
|
48
|
+
"Where agents go from tools to autonomous economic participants",
|
|
49
|
+
"Infrastructure for the agent age — identity, work, and value on-chain",
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const TWEET_SUFFIX = "#NaraChain @NaraBuildAI";
|
|
53
|
+
|
|
54
|
+
function pick(arr: string[]): string {
|
|
55
|
+
return arr[Math.floor(Math.random() * arr.length)]!;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Generate a random tweet for bind-twitter (includes agent ID). */
|
|
59
|
+
export function randomBindTweet(agentId: string): string {
|
|
60
|
+
return `Claiming my AI agent "${agentId}" on #NaraChain @NaraBuildAI — ${pick(TWEET_SLOGANS)}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Generate a random tweet for submit-tweet (general promotion). */
|
|
64
|
+
export function randomSubmitTweet(): string {
|
|
65
|
+
return `${pick(TWEET_PREFIXES)} — ${pick(TWEET_SLOGANS)} ${TWEET_SUFFIX}`;
|
|
66
|
+
}
|