@yamo/cli 1.3.12 → 1.3.14

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/index.js CHANGED
@@ -1,337 +1,72 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
- Object.defineProperty(o, "default", { enumerable: true, value: v });
16
- }) : function(o, v) {
17
- o["default"] = v;
18
- });
19
- var __importStar = (this && this.__importStar) || (function () {
20
- var ownKeys = function(o) {
21
- ownKeys = Object.getOwnPropertyNames || function (o) {
22
- var ar = [];
23
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
- return ar;
25
- };
26
- return ownKeys(o);
27
- };
28
- return function (mod) {
29
- if (mod && mod.__esModule) return mod;
30
- var result = {};
31
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
- __setModuleDefault(result, mod);
33
- return result;
34
- };
35
- })();
36
- var __importDefault = (this && this.__importDefault) || function (mod) {
37
- return (mod && mod.__esModule) ? mod : { "default": mod };
38
- };
39
3
  Object.defineProperty(exports, "__esModule", { value: true });
40
4
  const commander_1 = require("commander");
41
- const chalk_1 = __importDefault(require("chalk"));
42
- const crypto_1 = __importDefault(require("crypto"));
43
- const fs_1 = __importDefault(require("fs"));
44
- const path_1 = __importDefault(require("path"));
45
- const dotenv = __importStar(require("dotenv"));
46
5
  const core_1 = require("@yamo/core");
47
- const pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '../package.json'), 'utf8'));
48
- dotenv.config();
49
- // Constants
50
- const CONSTANTS = {
51
- HASH_PATTERN: /^0x[a-fA-F0-9]{64}$/,
52
- GENESIS_HASH: "0x0000000000000000000000000000000000000000000000000000000000000000",
53
- DEFAULT_FILENAME: "block.yamo",
54
- DEFAULT_CONSENSUS: "cli_manual",
55
- DEFAULT_LEDGER: "yamo_cli",
56
- DEFAULT_INTENT: "execute_task",
57
- HEX_PREFIX: "0x",
58
- HASH_ALGORITHM: "sha256",
59
- };
6
+ const config_js_1 = require("./utils/config.js");
7
+ const constants_js_1 = require("./utils/constants.js");
8
+ const hash_js_1 = require("./commands/hash.js");
9
+ const init_js_1 = require("./commands/init.js");
10
+ const submit_js_1 = require("./commands/submit.js");
11
+ const audit_js_1 = require("./commands/audit.js");
12
+ const download_bundle_js_1 = require("./commands/download-bundle.js");
13
+ const config_js_2 = require("./commands/config.js");
14
+ const bridge_js_1 = require("./commands/bridge.js");
60
15
  const program = new commander_1.Command();
61
- const ipfsManager = new core_1.IpfsManager();
62
- const chainClient = new core_1.YamoChainClient();
63
- // Hash utilities
64
- const hash = {
65
- sha256: (content) => {
66
- return crypto_1.default.createHash(CONSTANTS.HASH_ALGORITHM).update(content).digest("hex");
67
- },
68
- bytes32: (content) => {
69
- return `${CONSTANTS.HEX_PREFIX}${hash.sha256(content)}`;
70
- },
71
- };
72
- // Response formatting helpers
73
- const format = {
74
- success: (msg) => console.log(chalk_1.default.green(msg)),
75
- error: (msg) => console.error(chalk_1.default.red(`Error: ${msg}`)),
76
- info: (msg) => console.log(chalk_1.default.blue(msg)),
77
- warn: (msg) => console.log(chalk_1.default.yellow(msg)),
78
- detail: (msg) => console.log(chalk_1.default.gray(msg)),
79
- value: (msg) => console.log(chalk_1.default.cyan(msg)),
80
- };
81
- // Error handling helper
82
- function handleCommandError(error, context) {
83
- if (error instanceof Error) {
84
- const message = context ? `${context}: ${error.message}` : error.message;
85
- format.error(message);
86
- }
87
- else {
88
- format.error("Unknown error occurred");
89
- }
90
- }
91
- // Validation helpers
92
- function validateBytes32(value, fieldName) {
93
- if (!value.match(CONSTANTS.HASH_PATTERN)) {
94
- throw new Error(`${fieldName} must be a valid bytes32 hash (0x + 64 hex chars). ` +
95
- `Received: ${value.substring(0, 20)}...` +
96
- `\nDo NOT include algorithm prefixes like "sha256:"`);
97
- }
98
- }
99
- function validateBlockId(blockId) {
100
- if (!blockId)
101
- throw new Error("blockId is required");
102
- const parts = blockId.split('_');
103
- if (parts.length < 2) {
104
- throw new Error(`blockId must follow format {origin}_{workflow} (e.g., 'claude_chain'). Received: ${blockId}`);
105
- }
106
- }
107
- // Submit command helpers
108
- async function validateEncryptionKey(key) {
109
- const { validatePasswordStrength } = await import("@yamo/core");
110
- try {
111
- validatePasswordStrength(key);
112
- }
113
- catch (e) {
114
- format.error("Password validation failed:");
115
- format.error(e.message);
116
- format.warn("\nKey requirements:");
117
- console.error(" • Minimum 12 characters");
118
- console.error(" • Mix of uppercase, lowercase, numbers, symbols");
119
- console.error(" • Avoid common patterns (password, 123456, qwerty)");
120
- throw e;
121
- }
122
- }
123
- function prepareIpfsFiles(content, file) {
124
- const files = [{ name: CONSTANTS.DEFAULT_FILENAME, content }];
125
- const outputMatch = content.match(/output:\s*([^;]+);/);
126
- if (outputMatch) {
127
- const artifactName = outputMatch[1].trim();
128
- // Security: Check for path traversal patterns
129
- if (artifactName.includes('..') || artifactName.startsWith('/')) {
130
- throw new Error(`Invalid artifact name: ${artifactName} (path-like names are not allowed)`);
131
- }
132
- const artifactPath = path_1.default.join(path_1.default.dirname(file), artifactName);
133
- const resolvedPath = path_1.default.resolve(artifactPath);
134
- const inputDir = path_1.default.resolve(path_1.default.dirname(file));
135
- if (!resolvedPath.startsWith(inputDir)) {
136
- throw new Error(`Artifact path outside allowed directory: ${artifactName}`);
137
- }
138
- if (fs_1.default.existsSync(resolvedPath)) {
139
- format.info(`Bundling output: ${artifactName}`);
140
- files.push({ name: artifactName, content: fs_1.default.readFileSync(resolvedPath, "utf8") });
141
- }
142
- }
143
- return files;
144
- }
145
- async function resolvePreviousBlock(prev) {
146
- if (prev) {
147
- validateBytes32(prev, "previousBlock");
148
- return prev;
149
- }
150
- format.info("[INFO] No previousBlock provided, fetching latest block from chain...");
151
- const latestHash = await chainClient.getLatestBlockHash();
152
- if (latestHash && latestHash !== CONSTANTS.GENESIS_HASH) {
153
- format.success(`[INFO] Using latest block's contentHash: ${latestHash}`);
154
- return latestHash;
155
- }
156
- format.warn("[INFO] No existing blocks found, using genesis");
157
- return CONSTANTS.GENESIS_HASH;
158
- }
159
16
  program
160
- .name("yamo")
161
- .description("YAMO Protocol CLI - Manage Agentic Reasoning Chains")
162
- .version(pkg.version);
17
+ .name('yamo')
18
+ .description('YAMO CLI - Blockchain-anchored agent workflow system')
19
+ .version('1.3.14');
163
20
  program
164
- .command("hash")
165
- .description("Calculate the content hash of a YAMO block")
166
- .argument("<file>", "Path to the YAMO file")
167
- .action((file) => {
168
- try {
169
- const content = fs_1.default.readFileSync(file, "utf8").trim();
170
- const contentHash = hash.bytes32(content);
171
- format.success("Block Content Hash:");
172
- format.value(contentHash);
173
- }
174
- catch (error) {
175
- handleCommandError(error);
176
- }
177
- });
21
+ .command('hash')
22
+ .description('Calculate SHA256 hash of a file')
23
+ .argument('<file>', 'Path to the file')
24
+ .action(hash_js_1.hashCommand);
178
25
  program
179
- .command("init")
180
- .description("Initialize a new YAMO block template")
181
- .argument("<agent_name>", "Name of the agent")
182
- .option("-i, --intent <intent>", "Agent intent", CONSTANTS.DEFAULT_INTENT)
183
- .action((agent_name, options) => {
184
- try {
185
- const template = `
186
- agent: ${agent_name};
187
- intent: ${options.intent};
188
- context:
189
- platform;yamo_v0.5;
190
- constraints:
191
- - human_readable;true;
192
- priority: medium;
193
- output: result.json;
194
- meta: hypothesis;Initial hypothesis;
195
- meta: confidence;0.9;
196
- log: session_start;timestamp;${new Date().toISOString()};
197
- handoff: User;
198
- `.trim();
199
- fs_1.default.writeFileSync(CONSTANTS.DEFAULT_FILENAME, template);
200
- format.success(`Created YAMO template: ${chalk_1.default.bold(CONSTANTS.DEFAULT_FILENAME)}`);
201
- }
202
- catch (error) {
203
- handleCommandError(error);
204
- }
205
- });
26
+ .command('init')
27
+ .description('Create a new YAMO block template')
28
+ .argument('<agent_name>', 'Name of the agent')
29
+ .option('--intent <intent>', 'Intent description', constants_js_1.CONSTANTS.DEFAULT_INTENT)
30
+ .action(init_js_1.initCommand);
206
31
  program
207
- .command("submit")
208
- .description("Submit a YAMO block to the blockchain")
209
- .argument("<file>", "Path to the YAMO file")
210
- .requiredOption("--id <blockId>", "Unique Block ID")
211
- .option("--prev <previousBlock>", "Previous Block Hash (omits to auto-fetch from chain)")
212
- .option("--consensus <type>", "Consensus Type", CONSTANTS.DEFAULT_CONSENSUS)
213
- .option("--ledger <name>", "Ledger Name", CONSTANTS.DEFAULT_LEDGER)
214
- .option("--ipfs", "Upload content to IPFS before submitting")
215
- .option("-e, --encrypt", "Encrypt the bundle")
216
- .option("-k, --key <key>", "Encryption key (or set YAMO_ENCRYPTION_KEY)")
217
- .action(async (file, options) => {
218
- try {
219
- // Validate inputs
220
- validateBlockId(options.id);
221
- // Validate encryption key if needed
222
- if (options.encrypt) {
223
- const key = options.key || process.env.YAMO_ENCRYPTION_KEY;
224
- if (!key) {
225
- throw new Error("Encryption enabled but no key provided. Use --key or set YAMO_ENCRYPTION_KEY");
226
- }
227
- await validateEncryptionKey(key);
228
- }
229
- // Calculate content hash
230
- const content = fs_1.default.readFileSync(file, "utf8").trim();
231
- const contentHash = hash.bytes32(content);
232
- format.info(`Calculated Hash: ${contentHash}`);
233
- // Handle IPFS upload
234
- let ipfsCID;
235
- if (options.ipfs) {
236
- const files = prepareIpfsFiles(content, file);
237
- const encryptionKey = options.encrypt
238
- ? (options.key || process.env.YAMO_ENCRYPTION_KEY)
239
- : undefined;
240
- if (encryptionKey) {
241
- format.warn("🔒 Encrypting bundle...");
242
- }
243
- ipfsCID = await ipfsManager.upload({ content, files, encryptionKey });
244
- format.info(`IPFS Bundle CID: ${ipfsCID}`);
245
- }
246
- // Resolve previous block
247
- const resolvedPreviousBlock = await resolvePreviousBlock(options.prev);
248
- // Submit to blockchain
249
- await chainClient.submitBlock(options.id, resolvedPreviousBlock, contentHash, options.consensus, options.ledger, ipfsCID);
250
- }
251
- catch (error) {
252
- handleCommandError(error);
253
- }
32
+ .command('submit')
33
+ .description('Submit a YAMO block to the blockchain')
34
+ .argument('<file>', 'Path to the YAMO file')
35
+ .option('--id <blockId>', 'Unique Block ID (format: {origin}_{workflow})')
36
+ .option('--prev <previousBlock>', 'Previous block hash (auto-fetches if omitted)')
37
+ .option('--consensus <type>', 'Consensus mechanism', constants_js_1.CONSTANTS.DEFAULT_CONSENSUS)
38
+ .option('--ledger <name>', 'Ledger name', constants_js_1.CONSTANTS.DEFAULT_LEDGER)
39
+ .option('--ipfs', 'Upload content to IPFS', false)
40
+ .option('-e, --encrypt', 'Encrypt bundle before IPFS upload', false)
41
+ .option('-k, --key <key>', 'Encryption/decryption key')
42
+ .action((file, options) => {
43
+ const chainClient = new core_1.YamoChainClient(config_js_1.config.rpcUrl, config_js_1.config.privateKey, config_js_1.config.contractAddress);
44
+ const ipfsClient = new core_1.IpfsManager({ jwt: config_js_1.config.pinataJwt });
45
+ return (0, submit_js_1.submitCommand)(file, options, { chainClient, ipfsClient });
254
46
  });
255
47
  program
256
- .command("audit")
257
- .description("Audit a block's integrity (Chain vs IPFS)")
258
- .argument("<blockId>", "Block ID to audit")
259
- .option("-k, --key <key>", "Decryption key")
260
- .action(async (blockId, options) => {
261
- try {
262
- format.info(`Auditing Block ${blockId}...`);
263
- const block = await chainClient.getBlock(blockId);
264
- if (!block) {
265
- format.error("Block not found on-chain.");
266
- return;
267
- }
268
- format.detail("Found on-chain record:");
269
- console.log(` Agent: ${block.agentAddress}`);
270
- console.log(` Hash: ${block.contentHash}`);
271
- console.log(` IPFS: ${block.ipfsCID || "None"}`);
272
- if (!block.ipfsCID) {
273
- format.warn("⚠️ No IPFS CID. Cannot perform deep content audit.");
274
- return;
275
- }
276
- format.info("Fetching content from IPFS...");
277
- const key = options.key || process.env.YAMO_ENCRYPTION_KEY;
278
- const content = await ipfsManager.download(block.ipfsCID, key);
279
- const calcHash = hash.bytes32(content);
280
- console.log(` Calculated: ${calcHash}`);
281
- if (calcHash === block.contentHash) {
282
- format.success("✅ INTEGRITY VERIFIED: Content matches chain hash.");
283
- }
284
- else {
285
- format.error("❌ INTEGRITY FAILED: Hash mismatch!");
286
- console.log(` Expected: ${block.contentHash}`);
287
- console.log(` Got: ${calcHash}`);
288
- }
289
- }
290
- catch (error) {
291
- handleCommandError(error);
292
- }
48
+ .command('audit')
49
+ .description('Verify a block on the blockchain')
50
+ .argument('<blockId>', 'Block ID to audit')
51
+ .option('-k, --key <key>', 'Decryption key')
52
+ .action((blockId, options) => {
53
+ const chainClient = new core_1.YamoChainClient(config_js_1.config.rpcUrl, config_js_1.config.privateKey, config_js_1.config.contractAddress);
54
+ const ipfsClient = new core_1.IpfsManager({ jwt: config_js_1.config.pinataJwt });
55
+ return (0, audit_js_1.auditCommand)(blockId, options, { chainClient, ipfsClient });
293
56
  });
294
57
  program
295
- .command("download-bundle")
296
- .description("Download complete IPFS bundle including all artifacts")
297
- .argument("<cid>", "IPFS CID to download")
298
- .option("-k, --key <key>", "Decryption key (if encrypted)")
299
- .option("-o, --output <dir>", "Output directory (default: ./bundle_<cid>)", "./bundle_<cid>")
300
- .action(async (cid, options) => {
301
- try {
302
- format.info(`Downloading bundle ${cid}...`);
303
- const { IpfsManager } = await import("@yamo/core");
304
- const ipfs = new IpfsManager();
305
- const key = options.key || process.env.YAMO_ENCRYPTION_KEY;
306
- const bundle = await ipfs.downloadBundle(cid, key);
307
- // Create output directory
308
- const outputDir = options.output.replace("<cid>", cid.substring(0, 8));
309
- if (!fs_1.default.existsSync(outputDir)) {
310
- fs_1.default.mkdirSync(outputDir, { recursive: true });
311
- }
312
- // Write block.yamo
313
- fs_1.default.writeFileSync(path_1.default.join(outputDir, CONSTANTS.DEFAULT_FILENAME), bundle.block);
314
- format.success(`✓ Downloaded ${CONSTANTS.DEFAULT_FILENAME}`);
315
- // Write metadata
316
- if (bundle.metadata) {
317
- fs_1.default.writeFileSync(path_1.default.join(outputDir, "metadata.json"), JSON.stringify(bundle.metadata, null, 2));
318
- format.success("✓ Downloaded metadata.json");
319
- }
320
- // Write artifact files
321
- for (const [filename, content] of Object.entries(bundle.files)) {
322
- const filePath = path_1.default.join(outputDir, filename);
323
- fs_1.default.writeFileSync(filePath, content);
324
- format.success(`✓ Downloaded ${filename}`);
325
- }
326
- format.success(`\nBundle saved to: ${outputDir}`);
327
- format.detail(`Files: ${1 + Object.keys(bundle.files).length} total`);
328
- if (bundle.metadata?.hasEncryption) {
329
- format.warn("🔒 Bundle was decrypted using provided key");
330
- }
331
- }
332
- catch (error) {
333
- handleCommandError(error);
334
- format.detail("\nIf the bundle is encrypted, provide --key or set YAMO_ENCRYPTION_KEY");
335
- }
336
- });
58
+ .command('config')
59
+ .description('Manage local configuration and secrets')
60
+ .argument('<action>', 'Action: set, get, list, remove')
61
+ .argument('[key]', 'Configuration key')
62
+ .argument('[value]', 'Configuration value')
63
+ .action(config_js_2.configCommand);
64
+ program
65
+ .command('download-bundle')
66
+ .description('Download bundle from IPFS')
67
+ .argument('<cid>', 'IPFS content identifier')
68
+ .requiredOption('-o, --output <path>', 'Output file path')
69
+ .option('-k, --key <key>', 'Decryption key')
70
+ .action(download_bundle_js_1.downloadBundleCommand);
71
+ program.addCommand((0, bridge_js_1.bridgeCommand)());
337
72
  program.parse();
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.validateConfig = exports.config = void 0;
37
+ const dotenv = __importStar(require("dotenv"));
38
+ const storage_js_1 = require("./storage.js");
39
+ // Load environment variables
40
+ dotenv.config();
41
+ /**
42
+ * Centralized configuration for YAMO CLI.
43
+ * Loads and types environment variables with local storage fallback.
44
+ */
45
+ exports.config = {
46
+ rpcUrl: process.env.RPC_URL || storage_js_1.storage.get('RPC_URL') || 'http://127.0.0.1:8545',
47
+ privateKey: process.env.PRIVATE_KEY || storage_js_1.storage.get('PRIVATE_KEY'),
48
+ contractAddress: process.env.CONTRACT_ADDRESS || storage_js_1.storage.get('CONTRACT_ADDRESS'),
49
+ encryptionKey: process.env.YAMO_ENCRYPTION_KEY || storage_js_1.storage.get('YAMO_ENCRYPTION_KEY'),
50
+ pinataJwt: process.env.PINATA_JWT || storage_js_1.storage.get('PINATA_JWT'),
51
+ bridgeUrl: process.env.YAMO_BRIDGE_URL || storage_js_1.storage.get('YAMO_BRIDGE_URL'),
52
+ };
53
+ /**
54
+ * Validates that required configuration is present for specific operations.
55
+ */
56
+ exports.validateConfig = {
57
+ /**
58
+ * Ensure encryption key is available
59
+ */
60
+ requireEncryptionKey: (providedKey) => {
61
+ const key = providedKey || exports.config.encryptionKey;
62
+ if (!key) {
63
+ throw new Error('Encryption enabled but no key provided. Use --key or set YAMO_ENCRYPTION_KEY');
64
+ }
65
+ return key;
66
+ }
67
+ };
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONSTANTS = void 0;
4
+ exports.CONSTANTS = {
5
+ HASH_PATTERN: /^0x[a-fA-F0-9]{64}$/,
6
+ GENESIS_HASH: '0x0000000000000000000000000000000000000000000000000000000000000000',
7
+ DEFAULT_FILENAME: 'block.yamo',
8
+ DEFAULT_CONSENSUS: 'cli_manual',
9
+ DEFAULT_LEDGER: 'yamo_cli',
10
+ DEFAULT_INTENT: 'execute_task',
11
+ HEX_PREFIX: '0x',
12
+ HASH_ALGORITHM: 'sha256',
13
+ };
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.format = void 0;
7
+ exports.handleCommandError = handleCommandError;
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ exports.format = {
10
+ success(msg) {
11
+ console.log(chalk_1.default.green(msg));
12
+ },
13
+ error(msg) {
14
+ console.error(chalk_1.default.red(`[ERROR] ${msg}`));
15
+ },
16
+ info(msg) {
17
+ console.log(chalk_1.default.blue(msg));
18
+ },
19
+ warn(msg) {
20
+ console.error(chalk_1.default.yellow(`[WARN] ${msg}`));
21
+ },
22
+ detail(msg) {
23
+ console.log(chalk_1.default.gray(msg));
24
+ },
25
+ value(msg) {
26
+ console.log(chalk_1.default.cyan(msg));
27
+ },
28
+ };
29
+ /**
30
+ * Handles command errors with consistent formatting.
31
+ * @param error - Error object or unknown value
32
+ * @param context - Optional context message
33
+ */
34
+ function handleCommandError(error, context) {
35
+ if (error instanceof Error) {
36
+ const message = context ? `${context}: ${error.message}` : error.message;
37
+ exports.format.error(message);
38
+ }
39
+ else {
40
+ exports.format.error('Unknown error occurred');
41
+ }
42
+ process.exit(1);
43
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createSpinner = createSpinner;
7
+ exports.withSpinner = withSpinner;
8
+ const ora_1 = __importDefault(require("ora"));
9
+ /**
10
+ * Creates and starts a professional, emoji-free spinner.
11
+ * @param text - The text to display with the spinner
12
+ * @returns The spinner instance
13
+ */
14
+ function createSpinner(text) {
15
+ return (0, ora_1.default)({
16
+ text,
17
+ color: 'blue',
18
+ spinner: 'dots',
19
+ }).start();
20
+ }
21
+ /**
22
+ * Wraps a promise with a spinner.
23
+ * @param promise - The promise to track
24
+ * @param text - The text to display
25
+ * @returns The result of the promise
26
+ */
27
+ async function withSpinner(promise, text) {
28
+ const spinner = createSpinner(text);
29
+ try {
30
+ const result = await promise;
31
+ spinner.succeed(`[DONE] ${text}`);
32
+ return result;
33
+ }
34
+ catch (error) {
35
+ spinner.fail(`[FAILED] ${text}`);
36
+ throw error;
37
+ }
38
+ }
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.storage = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const CONFIG_DIR = process.env.YAMO_CONFIG_DIR || path_1.default.join(os_1.default.homedir(), '.yamo');
11
+ const CONFIG_FILE = path_1.default.join(CONFIG_DIR, 'config.json');
12
+ /**
13
+ * Handles persistent storage of local YAMO configuration.
14
+ */
15
+ exports.storage = {
16
+ /**
17
+ * Ensures the config directory exists.
18
+ */
19
+ ensureDir() {
20
+ if (!fs_1.default.existsSync(CONFIG_DIR)) {
21
+ fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
22
+ }
23
+ },
24
+ /**
25
+ * Reads the configuration file.
26
+ */
27
+ read() {
28
+ if (!fs_1.default.existsSync(CONFIG_FILE)) {
29
+ return {};
30
+ }
31
+ try {
32
+ const data = fs_1.default.readFileSync(CONFIG_FILE, 'utf8');
33
+ return JSON.parse(data);
34
+ }
35
+ catch {
36
+ return {};
37
+ }
38
+ },
39
+ /**
40
+ * Writes the configuration file.
41
+ */
42
+ write(config) {
43
+ this.ensureDir();
44
+ fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
45
+ },
46
+ /**
47
+ * Sets a value in the configuration.
48
+ */
49
+ set(key, value) {
50
+ const configData = this.read();
51
+ configData[key] = value;
52
+ this.write(configData);
53
+ },
54
+ /**
55
+ * Gets a value from the configuration.
56
+ */
57
+ get(key) {
58
+ const configData = this.read();
59
+ return configData[key];
60
+ },
61
+ /**
62
+ * Removes a key from the configuration.
63
+ */
64
+ remove(key) {
65
+ const configData = this.read();
66
+ const remaining = Object.fromEntries(Object.entries(configData).filter(([k]) => k !== key));
67
+ this.write(remaining);
68
+ },
69
+ };