@mimicprotocol/cli 0.0.1-rc.32 → 0.0.1-rc.35

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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @mimicprotocol/cli
2
2
 
3
+ ## 0.0.1-rc.35
4
+
5
+ ### Patch Changes
6
+
7
+ - 9a993b0: Add getNativeBalance
8
+ - da39b85: Added Tokens class
9
+ - 426a4fe: Environment refactored
10
+
11
+ ## 0.0.1-rc.34
12
+
13
+ ### Patch Changes
14
+
15
+ - 73a7900: Add login, logout and profiles commands
16
+ - d455822: Bump SDK version
17
+
18
+ ## 0.0.1-rc.33
19
+
20
+ ### Patch Changes
21
+
22
+ - 9530e1c: Revert test command directory change
23
+ - 21aba8d: Improve mocks error messages
24
+ - 5c8c562: Fix MockConfig validator
25
+
3
26
  ## 0.0.1-rc.32
4
27
 
5
28
  ### Patch Changes
package/README.md CHANGED
@@ -23,9 +23,10 @@
23
23
 
24
24
  ---
25
25
 
26
- ## Content
26
+ ## Content
27
27
 
28
28
  The `mimic` CLI is a command-line interface to:
29
+
29
30
  - Initialize a Mimic-compatible task project
30
31
  - Generate types from your task manifest and ABIs
31
32
  - Compile your AssemblyScript tasks to WebAssembly
@@ -35,7 +36,7 @@ The `mimic` CLI is a command-line interface to:
35
36
 
36
37
  ## Setup
37
38
 
38
- To set up this project you'll need [git](https://git-scm.com) and [yarn](https://classic.yarnpkg.com) installed.
39
+ To set up this project you'll need [git](https://git-scm.com) and [yarn](https://classic.yarnpkg.com) installed.
39
40
 
40
41
  Install the CLI from the root of the monorepo:
41
42
 
@@ -59,6 +60,9 @@ USAGE
59
60
  $ mimic [COMMAND]
60
61
 
61
62
  COMMANDS
63
+ login Authenticate with Mimic by storing your API key locally
64
+ logout Remove stored credentials for a profile
65
+ profiles List all configured authentication profiles
62
66
  codegen Generates typed interfaces for declared inputs and ABIs from your manifest.yaml file
63
67
  compile Compiles task
64
68
  test Tests your tasks
@@ -66,6 +70,69 @@ COMMANDS
66
70
  init Initializes a new Mimic-compatible project structure in the specified directory
67
71
  ```
68
72
 
73
+ ### Authentication
74
+
75
+ Before deploying tasks, you need to authenticate with your Mimic API key:
76
+
77
+ ```bash
78
+ # Interactive login (recommended)
79
+ $ mimic login
80
+
81
+ # Non-interactive login (for CI/CD)
82
+ $ mimic login --api-key YOUR_API_KEY
83
+
84
+ # Login with a specific profile
85
+ $ mimic login --profile staging -api-key YOUR_API_KEY
86
+ ```
87
+
88
+ #### Managing Profiles
89
+
90
+ The CLI supports multiple authentication profiles. Credentials are stored in `~/.mimic/credentials`.
91
+
92
+ ```bash
93
+ # List all configured profiles
94
+ $ mimic profiles
95
+
96
+ # Login with a specific profile
97
+ $ mimic login --profile production
98
+
99
+ # Deploy using a specific profile
100
+ $ mimic deploy --profile production
101
+
102
+ # Remove credentials for a profile
103
+ $ mimic logout --profile staging
104
+ ```
105
+
106
+ #### Credential Storage
107
+
108
+ Credentials are stored in an INI-style format at `~/.mimic/credentials`:
109
+
110
+ ```ini
111
+ [default]
112
+ api_key=YOUR_DEFAULT_KEY
113
+
114
+ [staging]
115
+ api_key=YOUR_STAGING_KEY
116
+
117
+ [production]
118
+ api_key=YOUR_PRODUCTION_KEY
119
+ ```
120
+
121
+ #### Deploy with Authentication
122
+
123
+ The `deploy` command now supports profile-based authentication:
124
+
125
+ ```bash
126
+ # Deploy using default profile
127
+ $ mimic deploy
128
+
129
+ # Deploy using a specific profile
130
+ $ mimic deploy --profile staging
131
+
132
+ # Deploy with explicit API key (overrides profile)
133
+ $ mimic deploy --api-key YOUR_API_KEY
134
+ ```
135
+
69
136
  For full CLI documentation and examples please visit [docs.mimic.fi](https://docs.mimic.fi/)
70
137
 
71
138
  ## Security
@@ -86,7 +153,6 @@ This project includes code from [The Graph Tooling](https://github.com/graphprot
86
153
  See the [LICENSE-MIT](https://github.com/graphprotocol/graph-tooling/blob/27659e56adfa3ef395ceaf39053dc4a31e6d86b7/LICENSE-MIT) file for details.
87
154
  Their original license and attribution are preserved.
88
155
 
89
-
90
156
  ---
91
157
 
92
158
  > Website [mimic.fi](https://mimic.fi)  · 
@@ -0,0 +1,54 @@
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
+ const core_1 = require("@oclif/core");
7
+ const CredentialsManager_1 = require("../lib/CredentialsManager");
8
+ const log_1 = __importDefault(require("../log"));
9
+ class Authenticate extends core_1.Command {
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ run() {
12
+ throw new Error('Method not implemented.');
13
+ }
14
+ authenticate(flags) {
15
+ let apiKey = flags['api-key'];
16
+ if (!apiKey) {
17
+ try {
18
+ const credentials = CredentialsManager_1.CredentialsManager.getDefault().getCredentials(flags.profile);
19
+ apiKey = credentials.apiKey;
20
+ }
21
+ catch (error) {
22
+ if (error instanceof Error) {
23
+ this.error(error.message, {
24
+ code: 'AuthenticationRequired',
25
+ suggestions: [
26
+ `Run ${log_1.default.highlightText('mimic login')} to authenticate`,
27
+ `Run ${log_1.default.highlightText(`mimic login --profile ${flags.profile ?? '<profile>'}`)} to create this profile`,
28
+ `Or use ${log_1.default.highlightText('--api-key')} flag to provide API key directly`,
29
+ ].filter(Boolean),
30
+ });
31
+ }
32
+ throw error;
33
+ }
34
+ }
35
+ return { apiKey };
36
+ }
37
+ }
38
+ Authenticate.description = 'Authenticate with Mimic by storing your API key locally';
39
+ Authenticate.examples = [
40
+ '<%= config.bin %> <%= command.id %>',
41
+ '<%= config.bin %> <%= command.id %> --profile staging',
42
+ '<%= config.bin %> <%= command.id %> --profile production --api-key YOUR_API_KEY',
43
+ ];
44
+ Authenticate.flags = {
45
+ profile: core_1.Flags.string({
46
+ char: 'p',
47
+ description: 'Profile name to use for this credential',
48
+ }),
49
+ 'api-key': core_1.Flags.string({
50
+ char: 'k',
51
+ description: 'API key (non-interactive mode)',
52
+ }),
53
+ };
54
+ exports.default = Authenticate;
@@ -44,13 +44,15 @@ const path_1 = require("path");
44
44
  const errors_1 = require("../errors");
45
45
  const packageManager_1 = require("../lib/packageManager");
46
46
  const log_1 = __importDefault(require("../log"));
47
+ const authenticate_1 = __importDefault(require("./authenticate"));
47
48
  const MIMIC_REGISTRY_DEFAULT = 'https://api-protocol.mimic.fi';
48
- class Deploy extends core_1.Command {
49
+ class Deploy extends authenticate_1.default {
49
50
  async run() {
50
51
  const { flags } = await this.parse(Deploy);
51
- const { key, input: inputDir, output: outputDir, 'skip-compile': skipCompile, url: registryUrl } = flags;
52
+ const { input: inputDir, output: outputDir, 'skip-compile': skipCompile, url: registryUrl } = flags;
52
53
  const fullInputDir = (0, path_1.resolve)(inputDir);
53
54
  const fullOutputDir = (0, path_1.resolve)(outputDir);
55
+ let credentials = this.authenticate(flags);
54
56
  if (!skipCompile) {
55
57
  const codegen = (0, packageManager_1.execBinCommand)('mimic', ['codegen'], process.cwd());
56
58
  if (codegen.status !== 0)
@@ -74,7 +76,7 @@ class Deploy extends core_1.Command {
74
76
  });
75
77
  }
76
78
  log_1.default.startAction('Uploading to Mimic Registry');
77
- const CID = await this.uploadToRegistry(neededFiles, key, registryUrl);
79
+ const CID = await this.uploadToRegistry(neededFiles, credentials, registryUrl);
78
80
  console.log(`IPFS CID: ${log_1.default.highlightText(CID)}`);
79
81
  log_1.default.stopAction();
80
82
  if (!fs.existsSync(fullOutputDir))
@@ -83,12 +85,12 @@ class Deploy extends core_1.Command {
83
85
  console.log(`CID saved at ${log_1.default.highlightText(fullOutputDir)}`);
84
86
  console.log(`Task deployed!`);
85
87
  }
86
- async uploadToRegistry(files, key, registryUrl) {
88
+ async uploadToRegistry(files, credentials, registryUrl) {
87
89
  try {
88
90
  const form = filesToForm(files);
89
91
  const { data } = await axios_1.default.post(`${registryUrl}/tasks`, form, {
90
92
  headers: {
91
- 'x-api-key': key,
93
+ 'x-api-key': credentials.apiKey,
92
94
  'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`,
93
95
  },
94
96
  });
@@ -114,9 +116,13 @@ class Deploy extends core_1.Command {
114
116
  }
115
117
  }
116
118
  Deploy.description = 'Uploads your compiled task artifacts to IPFS and registers it into the Mimic Registry';
117
- Deploy.examples = ['<%= config.bin %> <%= command.id %> --input ./dist --key MY_KEY --output ./dist'];
119
+ Deploy.examples = [
120
+ '<%= config.bin %> <%= command.id %> --input ./dist --output ./dist',
121
+ '<%= config.bin %> <%= command.id %> --profile staging',
122
+ '<%= config.bin %> <%= command.id %> --api-key MY_KEY --input ./dist --output ./dist',
123
+ ];
118
124
  Deploy.flags = {
119
- key: core_1.Flags.string({ char: 'k', description: 'Your account deployment key', required: true }),
125
+ ...authenticate_1.default.flags,
120
126
  input: core_1.Flags.string({ char: 'i', description: 'Directory containing the compiled artifacts', default: './build' }),
121
127
  output: core_1.Flags.string({ char: 'o', description: 'Output directory for deployment CID', default: './build' }),
122
128
  url: core_1.Flags.string({ char: 'u', description: `Mimic Registry base URL`, default: MIMIC_REGISTRY_DEFAULT }),
@@ -0,0 +1,102 @@
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
+ const prompts_1 = require("@inquirer/prompts");
7
+ const core_1 = require("@oclif/core");
8
+ const CredentialsManager_1 = require("../lib/CredentialsManager");
9
+ const log_1 = __importDefault(require("../log"));
10
+ const authenticate_1 = __importDefault(require("./authenticate"));
11
+ class Login extends authenticate_1.default {
12
+ async run() {
13
+ const { flags } = await this.parse(Login);
14
+ const { profile: profileInput, 'api-key': apiKeyFlag } = flags;
15
+ let apiKey;
16
+ let profileName = profileInput;
17
+ // Non-interactive mode
18
+ if (apiKeyFlag)
19
+ apiKey = apiKeyFlag;
20
+ else {
21
+ // Interactive mode
22
+ try {
23
+ apiKey = await (0, prompts_1.password)({
24
+ message: 'Enter your API key:',
25
+ mask: '*',
26
+ validate: (value) => {
27
+ if (!value || value.trim() === '')
28
+ return 'API key cannot be empty';
29
+ return true;
30
+ },
31
+ });
32
+ if (!profileName) {
33
+ profileName = await (0, prompts_1.input)({
34
+ message: `Enter a profile name (press Enter for "${CredentialsManager_1.CredentialsManager.getDefaultProfileName()}"):`,
35
+ default: CredentialsManager_1.CredentialsManager.getDefaultProfileName(),
36
+ validate: (value) => {
37
+ if (!value || value.trim() === '')
38
+ return 'Profile name cannot be empty';
39
+ if (value.includes('[') || value.includes(']') || value.includes('=')) {
40
+ return 'Profile name cannot contain [, ], or = characters';
41
+ }
42
+ return true;
43
+ },
44
+ });
45
+ }
46
+ }
47
+ catch (error) {
48
+ if (error instanceof Error && error.message.includes('User force closed')) {
49
+ console.log('\nLogin cancelled');
50
+ this.exit(0);
51
+ }
52
+ throw error;
53
+ }
54
+ }
55
+ this.saveAndConfirm(profileName || CredentialsManager_1.CredentialsManager.getDefaultProfileName(), apiKey, flags['force-login']);
56
+ }
57
+ async saveAndConfirm(profileName, apiKey, forceLogin) {
58
+ try {
59
+ const credentialsManager = CredentialsManager_1.CredentialsManager.getDefault();
60
+ if (credentialsManager.profileExists(profileName) && !forceLogin) {
61
+ const shouldOverwrite = await (0, prompts_1.confirm)({
62
+ message: `Profile ${log_1.default.highlightText(profileName)} already exists. Overwrite?`,
63
+ default: false,
64
+ });
65
+ if (!shouldOverwrite) {
66
+ console.log('Login cancelled');
67
+ return;
68
+ }
69
+ }
70
+ log_1.default.startAction('Saving credentials');
71
+ credentialsManager.saveProfile(profileName, apiKey);
72
+ log_1.default.stopAction();
73
+ console.log(`✓ Credentials saved for profile ${log_1.default.highlightText(profileName)}`);
74
+ console.log(` Location: ${log_1.default.highlightText('~/.mimic/credentials')}`);
75
+ console.log();
76
+ console.log(`You can now deploy tasks using: ${log_1.default.highlightText('mimic deploy')}`);
77
+ if (profileName !== CredentialsManager_1.CredentialsManager.getDefaultProfileName()) {
78
+ console.log(`Or with your profile: ${log_1.default.highlightText(`mimic deploy --profile ${profileName}`)}`);
79
+ }
80
+ }
81
+ catch (error) {
82
+ if (error instanceof Error)
83
+ this.error(`Failed to save credentials: ${error.message}`);
84
+ throw error;
85
+ }
86
+ }
87
+ }
88
+ Login.description = 'Authenticate with Mimic by storing your API key locally';
89
+ Login.examples = [
90
+ '<%= config.bin %> <%= command.id %>',
91
+ '<%= config.bin %> <%= command.id %> --profile staging',
92
+ '<%= config.bin %> <%= command.id %> --profile production --api-key YOUR_API_KEY',
93
+ ];
94
+ Login.flags = {
95
+ ...authenticate_1.default.flags,
96
+ 'force-login': core_1.Flags.boolean({
97
+ char: 'f',
98
+ description: 'Force login even if profile exists',
99
+ default: false,
100
+ }),
101
+ };
102
+ exports.default = Login;
@@ -0,0 +1,56 @@
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
+ const prompts_1 = require("@inquirer/prompts");
7
+ const core_1 = require("@oclif/core");
8
+ const CredentialsManager_1 = require("../lib/CredentialsManager");
9
+ const log_1 = __importDefault(require("../log"));
10
+ class Logout extends core_1.Command {
11
+ async run() {
12
+ const { flags } = await this.parse(Logout);
13
+ const { profile: profileName, force } = flags;
14
+ const profiles = CredentialsManager_1.CredentialsManager.getDefault().getProfiles();
15
+ if (!profiles.includes(profileName)) {
16
+ this.error(`Profile '${profileName}' does not exist`, {
17
+ code: 'ProfileNotFound',
18
+ suggestions: profiles.length > 0
19
+ ? [`Available profiles: ${profiles.join(', ')}`]
20
+ : ['No profiles found. Use `mimic login` to create one.'],
21
+ });
22
+ }
23
+ if (!force) {
24
+ const shouldRemove = await (0, prompts_1.confirm)({
25
+ message: `Are you sure you want to remove credentials for profile ${log_1.default.highlightText(profileName)}?`,
26
+ default: false,
27
+ });
28
+ if (!shouldRemove) {
29
+ console.log('Logout cancelled');
30
+ this.exit(0);
31
+ }
32
+ }
33
+ log_1.default.startAction(`Removing credentials for profile ${profileName}`);
34
+ CredentialsManager_1.CredentialsManager.getDefault().removeProfile(profileName);
35
+ log_1.default.stopAction();
36
+ console.log(`✓ Credentials removed for profile ${log_1.default.highlightText(profileName)}`);
37
+ }
38
+ }
39
+ Logout.description = 'Remove stored credentials for a profile';
40
+ Logout.examples = [
41
+ '<%= config.bin %> <%= command.id %>',
42
+ '<%= config.bin %> <%= command.id %> --profile staging',
43
+ ];
44
+ Logout.flags = {
45
+ profile: core_1.Flags.string({
46
+ char: 'p',
47
+ description: 'Profile name to remove',
48
+ default: CredentialsManager_1.CredentialsManager.getDefaultProfileName(),
49
+ }),
50
+ force: core_1.Flags.boolean({
51
+ char: 'f',
52
+ description: 'Skip confirmation prompt',
53
+ default: false,
54
+ }),
55
+ };
56
+ exports.default = Logout;
@@ -0,0 +1,29 @@
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
+ const core_1 = require("@oclif/core");
7
+ const CredentialsManager_1 = require("../lib/CredentialsManager");
8
+ const log_1 = __importDefault(require("../log"));
9
+ class Profiles extends core_1.Command {
10
+ async run() {
11
+ const profiles = CredentialsManager_1.CredentialsManager.getDefault().getProfiles();
12
+ if (profiles.length === 0) {
13
+ console.log('No profiles found.');
14
+ console.log();
15
+ console.log(`Run ${log_1.default.highlightText('mimic login')} to create your first profile.`);
16
+ return;
17
+ }
18
+ console.log(`Configured profiles (stored in ${log_1.default.highlightText('~/.mimic/credentials')}):`);
19
+ console.log();
20
+ for (const profile of profiles) {
21
+ console.log(`* ${log_1.default.highlightText(profile)}${profile === CredentialsManager_1.CredentialsManager.getDefaultProfileName() ? ' (default)' : ''}`);
22
+ }
23
+ console.log();
24
+ console.log(`Use ${log_1.default.highlightText('mimic deploy --profile <name>')} to deploy with a specific profile.`);
25
+ }
26
+ }
27
+ Profiles.description = 'List all configured authentication profiles';
28
+ Profiles.examples = ['<%= config.bin %> <%= command.id %>'];
29
+ exports.default = Profiles;
@@ -38,9 +38,8 @@ const path = __importStar(require("path"));
38
38
  const packageManager_1 = require("../lib/packageManager");
39
39
  class Test extends core_1.Command {
40
40
  async run() {
41
- const { args, flags } = await this.parse(Test);
42
- const { directory } = args;
43
- const { 'skip-compile': skipCompile } = flags;
41
+ const { flags } = await this.parse(Test);
42
+ const { directory, 'skip-compile': skipCompile } = flags;
44
43
  const baseDir = path.resolve(directory);
45
44
  const testPath = path.join(baseDir, 'tests');
46
45
  if (!skipCompile) {
@@ -56,11 +55,9 @@ class Test extends core_1.Command {
56
55
  }
57
56
  }
58
57
  Test.description = 'Runs task tests';
59
- Test.examples = ['<%= config.bin %> <%= command.id %> ./'];
60
- Test.args = {
61
- directory: core_1.Args.string({ description: 'task directory', required: false, default: './' }),
62
- };
58
+ Test.examples = ['<%= config.bin %> <%= command.id %> --directory ./'];
63
59
  Test.flags = {
60
+ directory: core_1.Flags.string({ char: 'd', description: 'task directory', default: './' }),
64
61
  'skip-compile': core_1.Flags.boolean({ description: 'skip codegen and compile steps' }),
65
62
  };
66
63
  exports.default = Test;
@@ -0,0 +1,181 @@
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.CredentialsManager = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const CREDENTIALS_FILE = 'credentials';
41
+ const DEFAULT_PROFILE = 'default';
42
+ class CredentialsManager {
43
+ static getDefault() {
44
+ return new CredentialsManager();
45
+ }
46
+ static getCredentialsFileName() {
47
+ return CREDENTIALS_FILE;
48
+ }
49
+ static getDefaultProfileName() {
50
+ return DEFAULT_PROFILE;
51
+ }
52
+ constructor(baseDir) {
53
+ this.baseDir = baseDir || path.join(os.homedir(), '.mimic');
54
+ }
55
+ getBaseDir() {
56
+ return this.baseDir;
57
+ }
58
+ getCredentialsPath() {
59
+ return path.join(this.getBaseDir(), CREDENTIALS_FILE);
60
+ }
61
+ createCredentialsDirIfNotExists() {
62
+ if (fs.existsSync(this.getBaseDir()))
63
+ return;
64
+ fs.mkdirSync(this.getBaseDir(), { recursive: true });
65
+ if (process.platform !== 'win32') {
66
+ try {
67
+ fs.chmodSync(this.getBaseDir(), 0o700);
68
+ }
69
+ catch { }
70
+ }
71
+ }
72
+ parseCredentials(content) {
73
+ const profiles = {};
74
+ const lines = content.split('\n');
75
+ let currentProfile = null;
76
+ for (const line of lines) {
77
+ const trimmed = line.trim();
78
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith(';'))
79
+ continue;
80
+ const profileMatch = trimmed.match(/^\[([^\]]+)\]$/);
81
+ if (profileMatch) {
82
+ currentProfile = profileMatch[1];
83
+ profiles[currentProfile] = { apiKey: '' };
84
+ continue;
85
+ }
86
+ if (currentProfile) {
87
+ const kvMatch = trimmed.match(/^([^=]+)=(.*)$/);
88
+ if (kvMatch) {
89
+ const key = kvMatch[1].trim();
90
+ const value = kvMatch[2].trim();
91
+ if (key === 'api_key')
92
+ profiles[currentProfile].apiKey = value;
93
+ }
94
+ }
95
+ }
96
+ return profiles;
97
+ }
98
+ serializeCredentials(profiles) {
99
+ const lines = [];
100
+ const profileEntries = Object.entries(profiles);
101
+ profileEntries.forEach(([profileName, credentials]) => {
102
+ lines.push(`[${profileName}]`);
103
+ lines.push(`api_key=${credentials.apiKey}`);
104
+ lines.push('');
105
+ });
106
+ return lines.join('\n');
107
+ }
108
+ readCredentials() {
109
+ const credentialsPath = this.getCredentialsPath();
110
+ if (!fs.existsSync(credentialsPath))
111
+ return {};
112
+ const content = fs.readFileSync(credentialsPath, 'utf-8');
113
+ return this.parseCredentials(content);
114
+ }
115
+ writeCredentials(profiles) {
116
+ this.createCredentialsDirIfNotExists();
117
+ const credentialsPath = this.getCredentialsPath();
118
+ const content = this.serializeCredentials(profiles);
119
+ fs.writeFileSync(credentialsPath, content, { mode: 0o600 });
120
+ if (process.platform !== 'win32') {
121
+ try {
122
+ fs.chmodSync(credentialsPath, 0o600);
123
+ }
124
+ catch { }
125
+ }
126
+ }
127
+ saveProfile(profileName, apiKey) {
128
+ const profiles = this.readCredentials();
129
+ profiles[profileName] = { apiKey };
130
+ this.writeCredentials(profiles);
131
+ }
132
+ getProfile(profileName = DEFAULT_PROFILE) {
133
+ const credentialsDir = this.getBaseDir();
134
+ const credentialsPath = this.getCredentialsPath();
135
+ if (!fs.existsSync(credentialsDir)) {
136
+ throw new Error(`No credentials directory found at ${credentialsDir}. Run 'mimic login' to authenticate.`);
137
+ }
138
+ if (!fs.existsSync(credentialsPath)) {
139
+ throw new Error(`No credentials file found. Run 'mimic login' to authenticate.`);
140
+ }
141
+ const profiles = this.readCredentials();
142
+ if (!profiles[profileName]) {
143
+ const availableProfiles = Object.keys(profiles);
144
+ const suggestion = availableProfiles.length > 0
145
+ ? `Available profiles: ${availableProfiles.join(', ')}`
146
+ : `No profiles found. Run 'mimic login' to create one.`;
147
+ throw new Error(`Profile '${profileName}' not found. ${suggestion}`);
148
+ }
149
+ const credentials = profiles[profileName];
150
+ if (!credentials.apiKey || credentials.apiKey.trim() === '') {
151
+ throw new Error(`Profile '${profileName}' has no API key. Run 'mimic login --profile ${profileName}' to update credentials.`);
152
+ }
153
+ return credentials;
154
+ }
155
+ getCredentials(profileName = DEFAULT_PROFILE) {
156
+ try {
157
+ return this.getProfile(profileName);
158
+ }
159
+ catch (error) {
160
+ if (error instanceof Error)
161
+ throw new Error(`Authentication required: ${error.message}`);
162
+ throw error;
163
+ }
164
+ }
165
+ getProfiles() {
166
+ const profiles = this.readCredentials();
167
+ return Object.keys(profiles);
168
+ }
169
+ removeProfile(profileName) {
170
+ const profiles = this.readCredentials();
171
+ if (!profiles[profileName])
172
+ throw new Error(`Profile '${profileName}' does not exist`);
173
+ delete profiles[profileName];
174
+ this.writeCredentials(profiles);
175
+ }
176
+ profileExists(profileName) {
177
+ const profiles = this.readCredentials();
178
+ return profileName in profiles;
179
+ }
180
+ }
181
+ exports.CredentialsManager = CredentialsManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mimicprotocol/cli",
3
- "version": "0.0.1-rc.32",
3
+ "version": "0.0.1-rc.35",
4
4
  "license": "GPL-3.0",
5
5
  "private": false,
6
6
  "type": "commonjs",