ic-mops 1.1.2-pre.0 → 1.2.0

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,17 @@
1
1
  # Mops CLI Changelog
2
2
 
3
+ ## 1.2.0
4
+ - Removed `mops transfer-ownership` command
5
+ - Added `mops owner` command to manage package owners ([docs](https://docs.mops.one/cli/mops-owner))
6
+ - Added `mops maintainers` command to manage package maintainers ([docs](https://docs.mops.one/cli/mops-maintainers))
7
+ - Added experimental support for pocket-ic replica that comes with dfx in `mops test` command ([docs](https://docs.mops.one/cli/mops-test#--replica))
8
+ - Added flag `--verbose` to `mops test` command to show replica logs
9
+ - Fixed bug where `mops watch` would fail if dfx.json did not exist
10
+ - Fixed bug with local dependencies without `mops.toml` file
11
+
12
+ ## 1.1.2
13
+ - Fixed `{MOPS_ENV}` substitution in local package path
14
+
3
15
  ## 1.1.1
4
16
  - `moc-wrapper` now adds hostname to the moc path cache(`.mops/moc-*` filename) to avoid errors when running in Dev Containers
5
17
  - `mops watch` now deploys canisters with the `--yes` flag to skip data loss confirmation
package/bundle/cli.tgz CHANGED
Binary file
package/cli.ts CHANGED
@@ -21,12 +21,13 @@ import {sync} from './commands/sync.js';
21
21
  import {outdated} from './commands/outdated.js';
22
22
  import {update} from './commands/update.js';
23
23
  import {bench} from './commands/bench.js';
24
- import {transferOwnership} from './commands/transfer-ownership.js';
25
24
  import {toolchain} from './commands/toolchain/index.js';
26
25
  import {Tool} from './types.js';
27
26
  import * as self from './commands/self.js';
28
27
  import {resolvePackages} from './resolve-packages.js';
29
28
  import {watch} from './commands/watch/watch.js';
29
+ import {addOwner, printOwners, removeOwner} from './commands/owner.js';
30
+ import {addMaintainer, printMaintainers, removeMaintainer} from './commands/maintainer.js';
30
31
 
31
32
  declare global {
32
33
  // eslint-disable-next-line no-var
@@ -214,7 +215,7 @@ program
214
215
  .addOption(new Option('--mode <mode>', 'Test mode').choices(['interpreter', 'wasi', 'replica']).default('interpreter'))
215
216
  .addOption(new Option('--replica <replica>', 'Which replica to use to run tests in replica mode').choices(['dfx', 'pocket-ic']))
216
217
  .option('-w, --watch', 'Enable watch mode')
217
- .option('--debug', 'Show debug logs')
218
+ .option('--verbose', 'Verbose output')
218
219
  .action(async (filter, options) => {
219
220
  checkConfigFile(true);
220
221
  await installAll({silent: true, lock: 'ignore', installFromLockFile: true});
@@ -290,6 +291,68 @@ userCommand
290
291
 
291
292
  program.addCommand(userCommand);
292
293
 
294
+ // mops owner *
295
+ const ownerCommand = new Command('owner').description('Package owner management');
296
+
297
+ // mops owner list
298
+ ownerCommand
299
+ .command('list')
300
+ .description('List package owners')
301
+ .action(async () => {
302
+ await printOwners();
303
+ });
304
+
305
+ // mops owner add
306
+ ownerCommand
307
+ .command('add <principal>')
308
+ .description('Add package owner')
309
+ .addOption(new Option('--yes', 'Do not ask for confirmation'))
310
+ .action(async (data, options) => {
311
+ await addOwner(data, options.yes);
312
+ });
313
+
314
+ // mops owner remove
315
+ ownerCommand
316
+ .command('remove <principal>')
317
+ .description('Remove package owner')
318
+ .addOption(new Option('--yes', 'Do not ask for confirmation'))
319
+ .action(async (data, options) => {
320
+ await removeOwner(data, options.yes);
321
+ });
322
+
323
+ program.addCommand(ownerCommand);
324
+
325
+ // mops maintainer *
326
+ const maintainerCommand = new Command('maintainer').description('Package maintainer management');
327
+
328
+ // mops maintainer list
329
+ maintainerCommand
330
+ .command('list')
331
+ .description('List package maintainers')
332
+ .action(async () => {
333
+ await printMaintainers();
334
+ });
335
+
336
+ // mops maintainer add
337
+ maintainerCommand
338
+ .command('add <principal>')
339
+ .description('Add package maintainer')
340
+ .addOption(new Option('--yes', 'Do not ask for confirmation'))
341
+ .action(async (data, options) => {
342
+ await addMaintainer(data, options.yes);
343
+ });
344
+
345
+ // mops maintainer remove
346
+ maintainerCommand
347
+ .command('remove <principal>')
348
+ .description('Remove package maintainer')
349
+ .addOption(new Option('--yes', 'Do not ask for confirmation'))
350
+ .action(async (data, options) => {
351
+ await removeMaintainer(data, options.yes);
352
+ });
353
+
354
+ program.addCommand(maintainerCommand);
355
+
293
356
  // bump
294
357
  program
295
358
  .command('bump [major|minor|patch]')
@@ -324,14 +387,6 @@ program
324
387
  await update(pkg, options);
325
388
  });
326
389
 
327
- // transfer-ownership
328
- program
329
- .command('transfer-ownership [to-principal]')
330
- .description('Transfer ownership of the current package to another principal')
331
- .action(async (toPrincipal) => {
332
- await transferOwnership(toPrincipal);
333
- });
334
-
335
390
  // toolchain
336
391
  const toolchainCommand = new Command('toolchain').description('Toolchain management');
337
392
 
@@ -1,5 +1,6 @@
1
1
  import process from 'node:process';
2
2
  import path from 'node:path';
3
+ import {existsSync} from 'node:fs';
3
4
  import {createLogUpdate} from 'log-update';
4
5
  import {getRootDir, readConfig} from '../../mops.js';
5
6
  import {installDeps} from './install-deps.js';
@@ -27,11 +28,17 @@ export async function installLocalDep(pkg : string, pkgPath = '', {verbose, sile
27
28
 
28
29
  // install dependencies
29
30
  if (!ignoreTransitive) {
30
- let dir = path.resolve(getRootDir(), pkgPath);
31
- let config = readConfig(path.join(dir, 'mops.toml'));
31
+ let dir = path.resolve(getRootDir(), pkgPath).replaceAll('{MOPS_ENV}', process.env.MOPS_ENV || 'local');
32
+ let mopsToml = path.join(dir, 'mops.toml');
33
+
34
+ if (!existsSync(mopsToml)) {
35
+ return true;
36
+ }
37
+
38
+ let config = readConfig(mopsToml);
32
39
  return installDeps(Object.values(config.dependencies || {}), {silent, verbose}, pkgPath);
33
40
  }
34
41
  else {
35
42
  return true;
36
43
  }
37
- }
44
+ }
@@ -0,0 +1,105 @@
1
+ import process from 'node:process';
2
+ import chalk from 'chalk';
3
+ import {checkConfigFile, getIdentity, readConfig} from '../mops.js';
4
+ import {mainActor} from '../api/actors.js';
5
+ import {Principal} from '@dfinity/principal';
6
+ import prompts from 'prompts';
7
+
8
+ export async function printMaintainers() {
9
+ if (!checkConfigFile()) {
10
+ return;
11
+ }
12
+
13
+ let config = readConfig();
14
+ let actor = await mainActor();
15
+
16
+ let maintainers = await actor.getPackageMaintainers(config.package?.name || '');
17
+ console.log(`Maintainers of package ${chalk.bold(config.package?.name)}:`);
18
+ for (let maintainer of maintainers) {
19
+ console.log(maintainer.toText());
20
+ }
21
+ }
22
+
23
+ export async function addMaintainer(maintainer : string, yes = false) {
24
+ if (!checkConfigFile()) {
25
+ return;
26
+ }
27
+
28
+ let config = readConfig();
29
+ let principal = Principal.fromText(maintainer);
30
+
31
+ if (!yes) {
32
+ let promptsConfig = {
33
+ onCancel() {
34
+ console.log('aborted');
35
+ process.exit(0);
36
+ },
37
+ };
38
+
39
+ let {confirm} = await prompts({
40
+ type: 'confirm',
41
+ name: 'confirm',
42
+ message: `Are you sure you want to add maintainer ${chalk.yellow(maintainer)} to ${chalk.yellow(config.package?.name)} package?`,
43
+ initial: true,
44
+ }, promptsConfig);
45
+
46
+ if (!confirm) {
47
+ return;
48
+ }
49
+ }
50
+
51
+ let identity = await getIdentity();
52
+ let actor = await mainActor(identity);
53
+
54
+ let res = await actor.addMaintainer(config.package?.name || '', principal);
55
+ if ('ok' in res) {
56
+ console.log(chalk.green('Success!'));
57
+ console.log(`Added maintainer ${chalk.bold(maintainer)} to package ${chalk.bold(config.package?.name)}`);
58
+ }
59
+ else {
60
+ console.error(chalk.red('Error: ') + res.err);
61
+ process.exit(1);
62
+ }
63
+ }
64
+
65
+ export async function removeMaintainer(maintainer : string, yes = false) {
66
+ if (!checkConfigFile()) {
67
+ return;
68
+ }
69
+
70
+ let config = readConfig();
71
+ let principal = Principal.fromText(maintainer);
72
+
73
+ if (!yes) {
74
+ let promptsConfig = {
75
+ onCancel() {
76
+ console.log('aborted');
77
+ process.exit(0);
78
+ },
79
+ };
80
+
81
+ let {confirm} = await prompts({
82
+ type: 'confirm',
83
+ name: 'confirm',
84
+ message: `Are you sure you want to remove maintainer ${chalk.red(maintainer)} from ${chalk.red(config.package?.name)} package?`,
85
+ initial: true,
86
+ }, promptsConfig);
87
+
88
+ if (!confirm) {
89
+ return;
90
+ }
91
+ }
92
+
93
+ let identity = await getIdentity();
94
+ let actor = await mainActor(identity);
95
+
96
+ let res = await actor.removeMaintainer(config.package?.name || '', principal);
97
+ if ('ok' in res) {
98
+ console.log(chalk.green('Success!'));
99
+ console.log(`Removed maintainer ${chalk.bold(maintainer)} from package ${chalk.bold(config.package?.name)}`);
100
+ }
101
+ else {
102
+ console.error(chalk.red('Error: ') + res.err);
103
+ process.exit(1);
104
+ }
105
+ }
@@ -0,0 +1,105 @@
1
+ import process from 'node:process';
2
+ import chalk from 'chalk';
3
+ import {checkConfigFile, getIdentity, readConfig} from '../mops.js';
4
+ import {mainActor} from '../api/actors.js';
5
+ import {Principal} from '@dfinity/principal';
6
+ import prompts from 'prompts';
7
+
8
+ export async function printOwners() {
9
+ if (!checkConfigFile()) {
10
+ return;
11
+ }
12
+
13
+ let config = readConfig();
14
+ let actor = await mainActor();
15
+
16
+ let owners = await actor.getPackageOwners(config.package?.name || '');
17
+ console.log(`Owners of package ${chalk.bold(config.package?.name)}:`);
18
+ for (let owner of owners) {
19
+ console.log(owner.toText());
20
+ }
21
+ }
22
+
23
+ export async function addOwner(owner : string, yes = false) {
24
+ if (!checkConfigFile()) {
25
+ return;
26
+ }
27
+
28
+ let config = readConfig();
29
+ let principal = Principal.fromText(owner);
30
+
31
+ if (!yes) {
32
+ let promptsConfig = {
33
+ onCancel() {
34
+ console.log('aborted');
35
+ process.exit(0);
36
+ },
37
+ };
38
+
39
+ let {confirm} = await prompts({
40
+ type: 'confirm',
41
+ name: 'confirm',
42
+ message: `Are you sure you want to add owner ${chalk.yellow(owner)} to ${chalk.yellow(config.package?.name)} package?`,
43
+ initial: true,
44
+ }, promptsConfig);
45
+
46
+ if (!confirm) {
47
+ return;
48
+ }
49
+ }
50
+
51
+ let identity = await getIdentity();
52
+ let actor = await mainActor(identity);
53
+
54
+ let res = await actor.addOwner(config.package?.name || '', principal);
55
+ if ('ok' in res) {
56
+ console.log(chalk.green('Success!'));
57
+ console.log(`Added owner ${chalk.bold(owner)} to package ${chalk.bold(config.package?.name)}`);
58
+ }
59
+ else {
60
+ console.error(chalk.red('Error: ') + res.err);
61
+ process.exit(1);
62
+ }
63
+ }
64
+
65
+ export async function removeOwner(owner : string, yes = false) {
66
+ if (!checkConfigFile()) {
67
+ return;
68
+ }
69
+
70
+ let config = readConfig();
71
+ let principal = Principal.fromText(owner);
72
+
73
+ if (!yes) {
74
+ let promptsConfig = {
75
+ onCancel() {
76
+ console.log('aborted');
77
+ process.exit(0);
78
+ },
79
+ };
80
+
81
+ let {confirm} = await prompts({
82
+ type: 'confirm',
83
+ name: 'confirm',
84
+ message: `Are you sure you want to remove owner ${chalk.red(owner)} from ${chalk.red(config.package?.name)} package?`,
85
+ initial: true,
86
+ }, promptsConfig);
87
+
88
+ if (!confirm) {
89
+ return;
90
+ }
91
+ }
92
+
93
+ let identity = await getIdentity();
94
+ let actor = await mainActor(identity);
95
+
96
+ let res = await actor.removeOwner(config.package?.name || '', principal);
97
+ if ('ok' in res) {
98
+ console.log(chalk.green('Success!'));
99
+ console.log(`Removed owner ${chalk.bold(owner)} from package ${chalk.bold(config.package?.name)}`);
100
+ }
101
+ else {
102
+ console.error(chalk.red('Error: ') + res.err);
103
+ process.exit(1);
104
+ }
105
+ }
@@ -62,7 +62,7 @@ export class Replica {
62
62
  }
63
63
  if (data.toString().includes('Failed to bind socket to')) {
64
64
  console.error(chalk.red(data.toString()));
65
- console.log('Please run again after some time');
65
+ console.log('Please try again after some time');
66
66
  process.exit(11);
67
67
  }
68
68
  });
@@ -48,7 +48,7 @@ type TestOptions = {
48
48
  reporter : ReporterName;
49
49
  mode : TestMode;
50
50
  replica : ReplicaName;
51
- debug : boolean;
51
+ verbose : boolean;
52
52
  };
53
53
 
54
54
 
@@ -82,7 +82,7 @@ export async function test(filter = '', options : Partial<TestOptions> = {}) {
82
82
  }
83
83
 
84
84
  replica.type = replicaType;
85
- replica.verbose = !!options.debug;
85
+ replica.verbose = !!options.verbose;
86
86
 
87
87
  if (options.watch) {
88
88
  replica.ttl = 60 * 15; // 15 minutes
@@ -51,14 +51,14 @@ function readDfxJson() : DfxConfig | Record<string, never> {
51
51
 
52
52
  export function getMotokoCanisters() : Record<string, string> {
53
53
  let dfxJson = readDfxJson();
54
- return Object.fromEntries(Object.entries(dfxJson.canisters)
54
+ return Object.fromEntries(Object.entries(dfxJson.canisters || {})
55
55
  .filter(([_, canister]) => canister.type === 'motoko')
56
56
  .map(([name, canister]) => [name, canister.main ?? '']));
57
57
  }
58
58
 
59
59
  export function getMotokoCanistersWithDeclarations() : Record<string, string> {
60
60
  let dfxJson = readDfxJson();
61
- return Object.fromEntries(Object.entries(dfxJson.canisters)
61
+ return Object.fromEntries(Object.entries(dfxJson.canisters || {})
62
62
  .filter(([_, canister]) => canister.type === 'motoko' && canister.declarations)
63
63
  .map(([name, canister]) => [name, canister.main ?? '']));
64
64
  }
@@ -90,7 +90,7 @@ type Result_6 =
90
90
  variant {
91
91
  err: Err;
92
92
  ok: vec record {
93
- PackageName;
93
+ PackageName__1;
94
94
  PackageVersion__1;
95
95
  };
96
96
  };
@@ -106,18 +106,18 @@ type Result_4 =
106
106
  };
107
107
  type Result_3 =
108
108
  variant {
109
- err: Err;
110
- ok: FileId;
109
+ err: text;
110
+ ok;
111
111
  };
112
112
  type Result_2 =
113
113
  variant {
114
114
  err: Err;
115
- ok: PublishingId;
115
+ ok: FileId;
116
116
  };
117
117
  type Result_1 =
118
118
  variant {
119
- err: text;
120
- ok;
119
+ err: Err;
120
+ ok: PublishingId;
121
121
  };
122
122
  type Result =
123
123
  variant {
@@ -157,9 +157,12 @@ type PackageSummary__1 =
157
157
  downloadsInLast7Days: nat;
158
158
  downloadsTotal: nat;
159
159
  highestVersion: PackageVersion;
160
+ maintainers: vec User;
160
161
  owner: principal;
161
162
  ownerInfo: User;
163
+ owners: vec User;
162
164
  publication: PackagePublication;
165
+ publisher: User;
163
166
  quality: PackageQuality;
164
167
  };
165
168
  type PackageSummaryWithChanges__1 =
@@ -171,9 +174,12 @@ type PackageSummaryWithChanges__1 =
171
174
  downloadsInLast7Days: nat;
172
175
  downloadsTotal: nat;
173
176
  highestVersion: PackageVersion;
177
+ maintainers: vec User;
174
178
  owner: principal;
175
179
  ownerInfo: User;
180
+ owners: vec User;
176
181
  publication: PackagePublication;
182
+ publisher: User;
177
183
  quality: PackageQuality;
178
184
  };
179
185
  type PackageSummaryWithChanges =
@@ -185,9 +191,12 @@ type PackageSummaryWithChanges =
185
191
  downloadsInLast7Days: nat;
186
192
  downloadsTotal: nat;
187
193
  highestVersion: PackageVersion;
194
+ maintainers: vec User;
188
195
  owner: principal;
189
196
  ownerInfo: User;
197
+ owners: vec User;
190
198
  publication: PackagePublication;
199
+ publisher: User;
191
200
  quality: PackageQuality;
192
201
  };
193
202
  type PackageSummary =
@@ -198,9 +207,12 @@ type PackageSummary =
198
207
  downloadsInLast7Days: nat;
199
208
  downloadsTotal: nat;
200
209
  highestVersion: PackageVersion;
210
+ maintainers: vec User;
201
211
  owner: principal;
202
212
  ownerInfo: User;
213
+ owners: vec User;
203
214
  publication: PackagePublication;
215
+ publisher: User;
204
216
  quality: PackageQuality;
205
217
  };
206
218
  type PackageQuality =
@@ -243,9 +255,12 @@ type PackageDetails =
243
255
  downloadsTotal: nat;
244
256
  fileStats: PackageFileStatsPublic;
245
257
  highestVersion: PackageVersion;
258
+ maintainers: vec User;
246
259
  owner: principal;
247
260
  ownerInfo: User;
261
+ owners: vec User;
248
262
  publication: PackagePublication;
263
+ publisher: User;
249
264
  quality: PackageQuality;
250
265
  testStats: TestStats__1;
251
266
  versionHistory: vec PackageSummaryWithChanges__1;
@@ -263,7 +278,7 @@ type PackageConfigV3_Publishing =
263
278
  keywords: vec text;
264
279
  license: text;
265
280
  moc: text;
266
- name: PackageName__1;
281
+ name: PackageName;
267
282
  readme: text;
268
283
  repository: text;
269
284
  requirements: opt vec Requirement;
@@ -283,7 +298,7 @@ type PackageConfigV3 =
283
298
  keywords: vec text;
284
299
  license: text;
285
300
  moc: text;
286
- name: PackageName__1;
301
+ name: PackageName;
287
302
  readme: text;
288
303
  repository: text;
289
304
  requirements: vec Requirement;
@@ -301,6 +316,8 @@ type PackageChanges =
301
316
  };
302
317
  type Main =
303
318
  service {
319
+ addMaintainer: (PackageName__1, principal) -> (Result_3);
320
+ addOwner: (PackageName__1, principal) -> (Result_3);
304
321
  backup: () -> ();
305
322
  computeHashesForExistingFiles: () -> ();
306
323
  finishPublish: (PublishingId) -> (Result);
@@ -308,14 +325,14 @@ type Main =
308
325
  getBackupCanisterId: () -> (principal) query;
309
326
  getDefaultPackages: (text) ->
310
327
  (vec record {
311
- PackageName;
328
+ PackageName__1;
312
329
  PackageVersion__1;
313
330
  }) query;
314
331
  getDownloadTrendByPackageId: (PackageId) ->
315
332
  (vec DownloadsSnapshot__1) query;
316
- getDownloadTrendByPackageName: (PackageName) ->
333
+ getDownloadTrendByPackageName: (PackageName__1) ->
317
334
  (vec DownloadsSnapshot__1) query;
318
- getFileHashes: (PackageName, PackageVersion__1) -> (Result_8);
335
+ getFileHashes: (PackageName__1, PackageVersion__1) -> (Result_8);
319
336
  getFileHashesByPackageIds: (vec PackageId) ->
320
337
  (vec record {
321
338
  PackageId;
@@ -324,19 +341,22 @@ type Main =
324
341
  blob;
325
342
  };
326
343
  });
327
- getFileHashesQuery: (PackageName, PackageVersion__1) -> (Result_8) query;
328
- getFileIds: (PackageName, PackageVersion__1) -> (Result_7) query;
344
+ getFileHashesQuery: (PackageName__1, PackageVersion__1) ->
345
+ (Result_8) query;
346
+ getFileIds: (PackageName__1, PackageVersion__1) -> (Result_7) query;
329
347
  getHighestSemverBatch:
330
348
  (vec record {
331
- PackageName;
349
+ PackageName__1;
332
350
  PackageVersion__1;
333
351
  SemverPart;
334
352
  }) -> (Result_6) query;
335
- getHighestVersion: (PackageName) -> (Result_5) query;
353
+ getHighestVersion: (PackageName__1) -> (Result_5) query;
336
354
  getMostDownloadedPackages: () -> (vec PackageSummary) query;
337
355
  getMostDownloadedPackagesIn7Days: () -> (vec PackageSummary) query;
338
356
  getNewPackages: () -> (vec PackageSummary) query;
339
- getPackageDetails: (PackageName, PackageVersion__1) -> (Result_4) query;
357
+ getPackageDetails: (PackageName__1, PackageVersion__1) -> (Result_4) query;
358
+ getPackageMaintainers: (PackageName__1) -> (vec principal) query;
359
+ getPackageOwners: (PackageName__1) -> (vec principal) query;
340
360
  getPackagesByCategory: () -> (vec record {
341
361
  text;
342
362
  vec PackageSummary;
@@ -350,17 +370,18 @@ type Main =
350
370
  getTotalPackages: () -> (nat) query;
351
371
  getUser: (principal) -> (opt User__1) query;
352
372
  http_request: (Request) -> (Response) query;
353
- notifyInstall: (PackageName, PackageVersion__1) -> () oneway;
373
+ notifyInstall: (PackageName__1, PackageVersion__1) -> () oneway;
354
374
  notifyInstalls: (vec record {
355
- PackageName;
375
+ PackageName__1;
356
376
  PackageVersion__1;
357
377
  }) -> () oneway;
378
+ removeMaintainer: (PackageName__1, principal) -> (Result_3);
379
+ removeOwner: (PackageName__1, principal) -> (Result_3);
358
380
  restore: (nat) -> ();
359
381
  search: (Text, opt nat, opt nat) -> (vec PackageSummary, PageCount) query;
360
- setUserProp: (text, text) -> (Result_1);
361
- startFileUpload: (PublishingId, Text, nat, blob) -> (Result_3);
362
- startPublish: (PackageConfigV3_Publishing) -> (Result_2);
363
- transferOwnership: (PackageName, principal) -> (Result_1);
382
+ setUserProp: (text, text) -> (Result_3);
383
+ startFileUpload: (PublishingId, Text, nat, blob) -> (Result_2);
384
+ startPublish: (PackageConfigV3_Publishing) -> (Result_1);
364
385
  transformRequest: (HttpTransformArg) -> (HttpResponse) query;
365
386
  uploadBenchmarks: (PublishingId, Benchmarks) -> (Result);
366
387
  uploadFileChunk: (PublishingId, FileId, nat, blob) -> (Result);
@@ -410,7 +431,7 @@ type DepsStatus =
410
431
  };
411
432
  type DependencyV2 =
412
433
  record {
413
- name: PackageName__1;
434
+ name: PackageName;
414
435
  repo: text;
415
436
  version: text;
416
437
  };