@kinotic-ai/kinotic-cli 2.2.0 → 3.0.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.
Files changed (33) hide show
  1. package/README.md +1 -393
  2. package/dist/commands/create/project.d.ts +5 -1
  3. package/dist/commands/create/project.js +17 -11
  4. package/dist/commands/generate.d.ts +2 -2
  5. package/dist/commands/initialize.d.ts +4 -4
  6. package/dist/commands/login.d.ts +9 -0
  7. package/dist/commands/login.js +21 -0
  8. package/dist/commands/spawn/lint.d.ts +13 -0
  9. package/dist/commands/spawn/lint.js +51 -0
  10. package/dist/commands/synchronize.d.ts +5 -6
  11. package/dist/commands/synchronize.js +39 -38
  12. package/dist/internal/CliAuthenticator.d.ts +46 -0
  13. package/dist/internal/CliAuthenticator.js +211 -0
  14. package/dist/internal/Logger.d.ts +1 -1
  15. package/dist/internal/Utils.d.ts +0 -16
  16. package/dist/internal/Utils.js +0 -156
  17. package/dist/internal/spawn/FileSystemSpawnEngine.d.ts +23 -0
  18. package/dist/internal/spawn/FileSystemSpawnEngine.js +33 -0
  19. package/dist/internal/spawn/InquirerPropertyResolver.d.ts +9 -0
  20. package/dist/internal/spawn/InquirerPropertyResolver.js +30 -0
  21. package/dist/templates/entity/BaseRepository.liquid +2 -2
  22. package/dist/templates/spawns/project/package.json.liquid +2 -2
  23. package/dist/templates/spawns/project/spawn.json +10 -1
  24. package/package.json +19 -34
  25. package/bin/dev.cmd +0 -3
  26. package/bin/dev.js +0 -6
  27. package/bin/run.cmd +0 -3
  28. package/bin/run.js +0 -6
  29. package/dist/internal/spawn/SpawnConfig.d.ts +0 -31
  30. package/dist/internal/spawn/SpawnConfig.js +0 -12
  31. package/dist/internal/spawn/SpawnEngine.d.ts +0 -27
  32. package/dist/internal/spawn/SpawnEngine.js +0 -187
  33. package/oclif.manifest.json +0 -213
@@ -0,0 +1,30 @@
1
+ import { confirm, input, number, select } from '@inquirer/prompts';
2
+ /**
3
+ * Prompts the user on the terminal for spawn properties missing from the
4
+ * render context, choosing the prompt type from the property's schema.
5
+ */
6
+ export class InquirerPropertyResolver {
7
+ hasPrompted = false;
8
+ async resolve(key, schema, message, defaultValue) {
9
+ if (!this.hasPrompted) {
10
+ console.log('Please provide the following...\n');
11
+ this.hasPrompted = true;
12
+ }
13
+ if (schema.type === 'boolean') {
14
+ return confirm({ message, default: typeof defaultValue === 'boolean' ? defaultValue : undefined });
15
+ }
16
+ else if (schema.enum) {
17
+ return select({
18
+ message,
19
+ choices: schema.enum.map((v) => ({ value: v })),
20
+ default: defaultValue !== undefined ? String(defaultValue) : undefined
21
+ });
22
+ }
23
+ else if (schema.type === 'number' || schema.type === 'integer') {
24
+ return number({ message, default: typeof defaultValue === 'number' ? defaultValue : undefined });
25
+ }
26
+ else {
27
+ return input({ message, default: typeof defaultValue === 'string' ? defaultValue : undefined });
28
+ }
29
+ }
30
+ }
@@ -19,7 +19,7 @@ export class Base{{ entityName }}Repository extends EntityRepository<{{ entityNa
19
19
  this.shouldValidate = shouldValidate
20
20
  }
21
21
 
22
- protected async beforeSaveOrUpdate(entity: {{ entityName }}): Promise<{{ entityName }}> {
22
+ protected override async beforeSaveOrUpdate(entity: {{ entityName }}): Promise<{{ entityName }}> {
23
23
  if (this.shouldValidate) {
24
24
  return this.validate(entity)
25
25
  } else {
@@ -27,7 +27,7 @@ export class Base{{ entityName }}Repository extends EntityRepository<{{ entityNa
27
27
  }
28
28
  }
29
29
 
30
- protected async beforeBulkSaveOrUpdate(entities: {{ entityName }}[]): Promise<{{ entityName }}[]> {
30
+ protected override async beforeBulkSaveOrUpdate(entities: {{ entityName }}[]): Promise<{{ entityName }}[]> {
31
31
  if (this.shouldValidate) {
32
32
  const validatedEntities: {{ entityName }}[] = []
33
33
  for (let entity of entities) {
@@ -12,8 +12,8 @@
12
12
  "typescript": "^5.9.3"
13
13
  },
14
14
  "catalog": {
15
- "@kinotic-ai/core": "{{ kinoticApiVersion }}",
16
- "@kinotic-ai/persistence": "{{ kinoticApiVersion }}"
15
+ "@kinotic-ai/core": "{{ kinoticCoreVersion }}",
16
+ "@kinotic-ai/persistence": "{{ kinoticPersistenceVersion }}"
17
17
  },
18
18
  "workspaces": [
19
19
  "packages/*"
@@ -1,11 +1,20 @@
1
1
  {
2
2
  "globals": {
3
- "kinoticApiVersion": "^1.0.9"
3
+ "kinoticCoreVersion": "^1.9.1",
4
+ "kinoticPersistenceVersion": "^2.0.0"
4
5
  },
5
6
  "propertySchema":{
6
7
  "projectName": {
7
8
  "type": "string",
8
9
  "description": "The name of the project you want to create"
10
+ },
11
+ "organization": {
12
+ "type": "string",
13
+ "description": "The organization that owns the project"
14
+ },
15
+ "application": {
16
+ "type": "string",
17
+ "description": "The application the project belongs to"
9
18
  }
10
19
  }
11
20
  }
package/package.json CHANGED
@@ -1,56 +1,50 @@
1
1
  {
2
2
  "name": "@kinotic-ai/kinotic-cli",
3
- "version": "2.2.0",
3
+ "version": "3.0.0",
4
4
  "description": "Kinotic CLI provides the ability to build, deploy, and manage Kinotic applications that run on the Kinotic OS.",
5
5
  "author": "Kinotic Developers",
6
6
  "bin": {
7
7
  "kinotic": "./bin/run.js"
8
8
  },
9
9
  "homepage": "https://kinotic-ai",
10
- "repository": "https://github.com/kinotic-ai/kinotic",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/kinotic-ai/kinotic"
13
+ },
11
14
  "license": "Elastic License 2.0\"",
12
15
  "main": "dist/index.js",
13
16
  "type": "module",
14
17
  "files": [
15
18
  "/bin",
16
19
  "/dist",
17
- "/npm-shrinkwrap.json",
18
- "/oclif.manifest.json"
20
+ "/npm-shrinkwrap.json"
19
21
  ],
20
22
  "dependencies": {
21
23
  "@inquirer/prompts": "^8.3.2",
22
- "@kinotic-ai/core": "^1.1.1",
24
+ "@kinotic-ai/core": "^3.1.0",
23
25
  "@kinotic-ai/idl": "^1.0.9",
24
- "@kinotic-ai/os-api": "^1.1.0",
25
- "@kinotic-ai/persistence": "^1.2.0",
26
+ "@kinotic-ai/os-api": "^3.1.0",
27
+ "@kinotic-ai/persistence": "^3.1.0",
28
+ "@kinotic-ai/spawn": "^0.5.0",
26
29
  "@oclif/core": "^4.9.0",
27
- "@oclif/plugin-autocomplete": "^3.2.41",
28
- "@oclif/plugin-help": "^6",
29
- "@oclif/plugin-not-found": "^3.2.75",
30
- "@oclif/plugin-plugins": "^5.4.58",
31
- "@oclif/plugin-update": "^4.7.23",
32
- "@oclif/plugin-warn-if-update-available": "^3.1.55",
33
30
  "c12": "^3.3.3",
34
31
  "chalk": "^5.6.2",
35
32
  "execa": "^9.6.1",
36
33
  "glob": "^13.0.6",
37
34
  "graphql": "^16.13.1",
38
- "liquidjs": "10.25.1",
35
+ "liquidjs": "10.27.0",
39
36
  "open": "^11.0.0",
40
37
  "ora": "^9.3.0",
41
38
  "p-timeout": "^7.0.1",
42
39
  "radash": "^12.1.1",
43
- "reflect-metadata": "^0.2.2",
44
- "simple-git": "^3.33.0",
40
+ "simple-git": "3.36.0",
45
41
  "terminal-link": "^5.0.0",
46
42
  "ts-morph": "^27.0.2",
47
43
  "uuid": "^13.0.0",
48
44
  "ws": "^8.19.0",
49
- "yaml": "2.8.3",
50
- "zod": "^4.3.6"
45
+ "yaml": "2.8.3"
51
46
  },
52
47
  "devDependencies": {
53
- "@oclif/test": "^4.1.16",
54
48
  "@types/chai": "^5",
55
49
  "@types/inquirer": "^9.0.9",
56
50
  "@types/mocha": "^10.0.10",
@@ -59,48 +53,39 @@
59
53
  "@types/ws": "^8.18.1",
60
54
  "chai": "^6",
61
55
  "eslint": "^9",
62
- "eslint-config-oclif": "^6",
63
56
  "eslint-config-prettier": "^10",
64
57
  "mocha": "^11",
65
- "oclif": "^4.22.92",
66
58
  "shx": "^0.4.0",
59
+ "tsc-alias": "^1.8.16",
67
60
  "tslib": "^2.8.1",
68
61
  "tsx": "^4.21.0",
69
- "tsc-alias": "^1.8.16",
70
62
  "typescript": "^5.9.3"
71
63
  },
72
64
  "oclif": {
73
65
  "bin": "kinotic",
74
66
  "dirname": "kinotic",
75
67
  "commands": "./dist/commands",
76
- "plugins": [
77
- "@oclif/plugin-help",
78
- "@oclif/plugin-plugins",
79
- "@oclif/plugin-update",
80
- "@oclif/plugin-not-found",
81
- "@oclif/plugin-warn-if-update-available",
82
- "@oclif/plugin-autocomplete"
83
- ],
84
68
  "topicSeparator": " ",
85
69
  "topics": {
86
70
  "create": {
87
71
  "description": "Creates Kinotic projects, libraries, and applications"
72
+ },
73
+ "spawn": {
74
+ "description": "Works with Kinotic spawn templates"
88
75
  }
89
76
  }
90
77
  },
91
78
  "engines": {
92
79
  "node": ">=18.0.0"
93
80
  },
94
- "keywords": [
95
- "oclif"
96
- ],
81
+ "keywords": [],
97
82
  "types": "dist/index.d.ts",
98
83
  "scripts": {
99
84
  "build": "shx rm -rf dist && tsc -b && tsc-alias -f && pnpm run copy-files",
100
85
  "lint": "eslint . --ext .ts --config .eslintrc",
101
86
  "posttest": "pnpm lint",
102
87
  "test": "mocha --forbid-only \"test/**/*.test.ts\"",
103
- "version": "oclif readme && git add README.md",
88
+ "version": "git add README.md",
104
89
  "copy-files": "cp -r ./src/templates/ ./dist/templates/"
105
90
  }
106
91
  }
package/bin/dev.cmd DELETED
@@ -1,3 +0,0 @@
1
- @echo off
2
-
3
- node "%~dp0\dev" %*
package/bin/dev.js DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env tsx
2
- // eslint-disable-next-line node/shebang
3
- (async () => {
4
- const oclif = await import('@oclif/core')
5
- await oclif.execute({type: 'esm', development: true, dir: import.meta.url})
6
- })()
package/bin/run.cmd DELETED
@@ -1,3 +0,0 @@
1
- @echo off
2
-
3
- node "%~dp0\run" %*
package/bin/run.js DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- // eslint-disable-next-line node/shebang
3
- (async () => {
4
- const oclif = await import('@oclif/core')
5
- await oclif.execute({type: 'esm', dir: import.meta.url})
6
- })()
@@ -1,31 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const PropertySchemaSchema: z.ZodObject<{
3
- type: z.ZodOptional<z.ZodEnum<{
4
- string: "string";
5
- number: "number";
6
- boolean: "boolean";
7
- integer: "integer";
8
- }>>;
9
- description: z.ZodOptional<z.ZodString>;
10
- default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
11
- enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
12
- }, z.core.$strip>;
13
- export declare const SpawnConfigSchema: z.ZodObject<{
14
- inherits: z.ZodOptional<z.ZodString>;
15
- globals: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
16
- propertySchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
17
- type: z.ZodOptional<z.ZodEnum<{
18
- string: "string";
19
- number: "number";
20
- boolean: "boolean";
21
- integer: "integer";
22
- }>>;
23
- description: z.ZodOptional<z.ZodString>;
24
- default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
25
- enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
26
- }, z.core.$strip>>>;
27
- }, z.core.$strip>;
28
- export type PropertySchema = z.infer<typeof PropertySchemaSchema>;
29
- export type SpawnConfig = z.infer<typeof SpawnConfigSchema>;
30
- export type GlobalsType = Record<string, unknown>;
31
- export type PropertySchemaType = Record<string, PropertySchema>;
@@ -1,12 +0,0 @@
1
- import { z } from 'zod';
2
- export const PropertySchemaSchema = z.object({
3
- type: z.enum(['string', 'number', 'integer', 'boolean']).optional(),
4
- description: z.string().optional(),
5
- default: z.union([z.string(), z.number(), z.boolean()]).optional(),
6
- enum: z.array(z.string()).optional(),
7
- });
8
- export const SpawnConfigSchema = z.object({
9
- inherits: z.string().optional(),
10
- globals: z.record(z.string(), z.unknown()).optional(),
11
- propertySchema: z.record(z.string(), PropertySchemaSchema).optional(),
12
- });
@@ -1,27 +0,0 @@
1
- import { SpawnResolver } from './SpawnResolver.js';
2
- export default class SpawnEngine {
3
- private engine;
4
- private spawnResolver;
5
- constructor(resolver: SpawnResolver);
6
- /**
7
- * Renders the specified Spawn
8
- * A Spawn is a directory that contains templates, an optional spawn.json file, and template parameters in folder and filenames
9
- *
10
- * This is done by performing the following
11
- *
12
- * - Will recursively walk the spawn copying any files or directories encountered
13
- * -- If a template file is encountered, it will be parsed and rendered prior to copying
14
- *
15
- * @param spawn the name of the spawn to parse and render. This is the name of the directory containing the spawn.json
16
- * @param destination the target directory where rendered data will be sent
17
- * @param context the values to be provided to the templates while rendering
18
- * @return a promise containing all the original values plus any added during rendering
19
- */
20
- renderSpawn(spawn: string, destination: string, context?: Record<string, unknown>): Promise<Record<string, unknown> | undefined>;
21
- private promptForMissingProperties;
22
- private _renderFile;
23
- private _renderDirectory;
24
- private logDebug;
25
- private shouldIgnore;
26
- }
27
- export declare const spawnEngine: SpawnEngine;
@@ -1,187 +0,0 @@
1
- import fs from 'node:fs';
2
- import fsP from 'node:fs/promises';
3
- import path from 'node:path';
4
- import { Liquid } from 'liquidjs';
5
- import { confirm, input, number, select } from '@inquirer/prompts';
6
- import { SpawnConfigSchema } from './SpawnConfig.js';
7
- import { spawnResolver } from './SpawnResolver.js';
8
- function upperFirst(s) {
9
- return s.charAt(0).toUpperCase() + s.slice(1);
10
- }
11
- function camelCase(s) {
12
- return s
13
- .replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase())
14
- .replace(/^(.)/, (_, c) => c.toLowerCase());
15
- }
16
- export default class SpawnEngine {
17
- engine;
18
- spawnResolver;
19
- constructor(resolver) {
20
- this.spawnResolver = resolver;
21
- this.engine = new Liquid({ cache: true });
22
- this.engine.registerFilter('packageToPath', (v) => v.replaceAll('.', '/'));
23
- this.engine.registerFilter('encodePackage', (v) => {
24
- v = v.replaceAll('-', '_');
25
- v = v.replace(/\.(\d+)/g, '._$1');
26
- return v;
27
- });
28
- this.engine.registerFilter('camelCase', (v) => camelCase(v));
29
- this.engine.registerFilter('upperFirst', (v) => upperFirst(v));
30
- }
31
- /**
32
- * Renders the specified Spawn
33
- * A Spawn is a directory that contains templates, an optional spawn.json file, and template parameters in folder and filenames
34
- *
35
- * This is done by performing the following
36
- *
37
- * - Will recursively walk the spawn copying any files or directories encountered
38
- * -- If a template file is encountered, it will be parsed and rendered prior to copying
39
- *
40
- * @param spawn the name of the spawn to parse and render. This is the name of the directory containing the spawn.json
41
- * @param destination the target directory where rendered data will be sent
42
- * @param context the values to be provided to the templates while rendering
43
- * @return a promise containing all the original values plus any added during rendering
44
- */
45
- async renderSpawn(spawn, destination, context) {
46
- let contextInternal;
47
- if (!fs.existsSync(destination)) {
48
- const source = await this.spawnResolver.resolveSpawn(spawn);
49
- const sources = [source];
50
- const currentConfigPath = path.resolve(source, 'spawn.json');
51
- if (fs.existsSync(currentConfigPath)) {
52
- const spawns = [];
53
- let currentConfig = currentConfigPath;
54
- let currentSpawn = SpawnConfigSchema.parse(JSON.parse(fs.readFileSync(currentConfig, { encoding: 'utf8' })));
55
- spawns.push(currentSpawn);
56
- // follow inheritance and build stack
57
- while (currentSpawn.inherits) {
58
- const inheritDir = path.resolve(path.dirname(currentConfig), currentSpawn.inherits);
59
- currentConfig = path.resolve(inheritDir, 'spawn.json');
60
- this.logDebug(`Inheriting from ${currentConfig}\n`);
61
- if (!fs.existsSync(currentConfig)) {
62
- throw new Error(`Inherited spawn ${currentConfig} does not exist`);
63
- }
64
- currentSpawn = SpawnConfigSchema.parse(JSON.parse(fs.readFileSync(currentConfig, { encoding: 'utf8' })));
65
- spawns.push(currentSpawn);
66
- sources.push(inheritDir);
67
- }
68
- let globals = {};
69
- let propertySchemas = {};
70
- for (const spawnConfig of spawns.reverse()) {
71
- if (spawnConfig.globals) {
72
- globals = { ...globals, ...spawnConfig.globals };
73
- }
74
- if (spawnConfig.propertySchema) {
75
- propertySchemas = { ...propertySchemas, ...spawnConfig.propertySchema };
76
- }
77
- }
78
- contextInternal = { ...globals, ...context };
79
- contextInternal = await this.promptForMissingProperties(propertySchemas, contextInternal);
80
- }
81
- await fsP.mkdir(destination, { recursive: true });
82
- for (const src of sources.reverse()) {
83
- await this._renderDirectory(src, src, destination, contextInternal);
84
- }
85
- return contextInternal;
86
- }
87
- else {
88
- throw new Error(`The target directory ${destination} already exists`);
89
- }
90
- }
91
- async promptForMissingProperties(propertySchema, context) {
92
- const ret = context ? { ...context } : {};
93
- let hasPrompted = false;
94
- for (const key in propertySchema) {
95
- if (!Object.prototype.hasOwnProperty.call(ret, key)) {
96
- if (!hasPrompted) {
97
- console.log('Please provide the following...\n');
98
- hasPrompted = true;
99
- }
100
- const schema = propertySchema[key];
101
- let message;
102
- if (schema.description?.includes('{{')) {
103
- message = this.engine.parseAndRenderSync(schema.description, ret);
104
- }
105
- else {
106
- message = schema.description ?? key;
107
- }
108
- let defaultValue;
109
- if (typeof schema.default === 'string' && schema.default.includes('{{')) {
110
- defaultValue = this.engine.parseAndRenderSync(schema.default, ret);
111
- }
112
- else {
113
- defaultValue = schema.default;
114
- }
115
- if (schema.type === 'boolean') {
116
- ret[key] = await confirm({ message, default: typeof defaultValue === 'boolean' ? defaultValue : undefined });
117
- }
118
- else if (schema.enum) {
119
- ret[key] = await select({
120
- message,
121
- choices: schema.enum.map((v) => ({ value: v })),
122
- default: defaultValue !== undefined ? String(defaultValue) : undefined
123
- });
124
- }
125
- else if (schema.type === 'number' || schema.type === 'integer') {
126
- ret[key] = await number({ message, default: typeof defaultValue === 'number' ? defaultValue : undefined });
127
- }
128
- else {
129
- ret[key] = await input({ message, default: typeof defaultValue === 'string' ? defaultValue : undefined });
130
- }
131
- }
132
- }
133
- return ret;
134
- }
135
- async _renderFile(source, destination, errorIfExists, context) {
136
- if (destination.includes('{{')) {
137
- destination = await this.engine.parseAndRender(destination, context ?? {});
138
- }
139
- let overwritingFile = false;
140
- if ((errorIfExists) && fs.existsSync(destination)) {
141
- if (errorIfExists) {
142
- throw new Error(`The target file ${destination} already exists`);
143
- }
144
- overwritingFile = true;
145
- }
146
- let readStream;
147
- if (destination.endsWith('.liquid')) {
148
- destination = destination.substring(0, destination.length - 7);
149
- readStream = await this.engine.renderFileToNodeStream(source, context ?? {});
150
- }
151
- else {
152
- readStream = fs.createReadStream(source);
153
- }
154
- this.logDebug(`${overwritingFile ? 'Overwriting' : 'Writing'} File\n${source}\nto\n${destination}\n`);
155
- await fsP.mkdir(path.dirname(destination), { recursive: true });
156
- const writeStream = fs.createWriteStream(destination);
157
- readStream.pipe(writeStream);
158
- }
159
- async _renderDirectory(baseFrom, from, baseTo, context) {
160
- const files = await fsP.readdir(from, { withFileTypes: true });
161
- for (const file of files) {
162
- const filePath = path.resolve(from, file.name);
163
- const to = filePath.replace(baseFrom, baseTo);
164
- if (file.isFile()) {
165
- if (!this.shouldIgnore(file.name)) {
166
- await this._renderFile(filePath, to, false, context);
167
- }
168
- else {
169
- this.logDebug(`Skipping File\n${filePath}\n`);
170
- }
171
- }
172
- else {
173
- await this._renderDirectory(baseFrom, filePath, baseTo, context);
174
- }
175
- }
176
- }
177
- logDebug(message) {
178
- if (process.env.DEBUG) {
179
- console.debug(message);
180
- }
181
- }
182
- shouldIgnore(fileName) {
183
- const filesToSkip = ['.DS_Store', 'spawn.json'];
184
- return filesToSkip.includes(fileName);
185
- }
186
- }
187
- export const spawnEngine = new SpawnEngine(spawnResolver);