mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-11 18:48:27 +00:00
fix: cli init
This commit is contained in:
parent
3bdf051b32
commit
a3798db1e6
31
README.md
31
README.md
@ -64,17 +64,17 @@ Developers waste time re-explaining context to AI tools. Hit token limits in Cla
|
||||
CORE is an open-source unified, persistent memory layer for all your AI tools. Your context follows you from Cursor to Claude to ChatGPT to Claude Code. One knowledge graph remembers who said what, when, and why. Connect once, remember everywhere. Stop managing context and start building.
|
||||
|
||||
## 🚀 Get Started
|
||||
|
||||
**Build your unified memory graph in 5 minutes:**
|
||||
|
||||
1. **Sign Up** at [core.heysol.ai](https://core.heysol.ai) and create your account
|
||||
2. **Add your first memory** - share context about yourself
|
||||
|
||||
<img width="2088" height="1212" alt="first-memory" src="https://github.com/user-attachments/assets/ecfab88e-e91a-474d-9ef5-fc6c19b655a8" />
|
||||
|
||||
<img width="2088" height="1212" alt="first-memory" src="https://github.com/user-attachments/assets/ecfab88e-e91a-474d-9ef5-fc6c19b655a8" />
|
||||
|
||||
3. **Visualize your memory graph** and see how CORE automatically forms connections between facts
|
||||
5. **Test it out** - ask "What do you know about me?" in conversatio section
|
||||
6. Connect to your tools:
|
||||
4. **Test it out** - ask "What do you know about me?" in conversatio section
|
||||
5. Connect to your tools:
|
||||
- [Claude](https://docs.heysol.ai/providers/claude) & [Cursor](https://docs.heysol.ai/providers/cursor) - coding with context
|
||||
- [CLaude Code CLI](https://docs.heysol.ai/providers/claude-code) & [Codex CLI](https://docs.heysol.ai/providers/codex) - terminal-based coding with memory
|
||||
- [Add Browser Extension](https://docs.heysol.ai/providers/browser-extension) - bring your memory to any website
|
||||
@ -83,23 +83,23 @@ CORE is an open-source unified, persistent memory layer for all your AI tools. Y
|
||||
## 🧩 Key Features
|
||||
|
||||
### 🧠 **Unified, Portable Memory**:
|
||||
|
||||
Add and recall your memory across **Cursor, Windsurf, Claude Desktop, Claude Code, Gemini CLI, AWS's Kiro, VS Code, and Roo Code** via MCP
|
||||
|
||||

|
||||
|
||||
|
||||
### 🕸️ **Temporal + Reified Knowledge Graph**:
|
||||
|
||||
Remember the story behind every fact—track who said what, when, and why with rich relationships and full provenance, not just flat storage
|
||||
|
||||

|
||||
|
||||
|
||||
### 🌐 **Browser Extension**:
|
||||
|
||||
Save conversations and content from ChatGPT, Grok, Gemini, Twitter, YouTube, blog posts, and any webpage directly into your CORE memory.
|
||||
|
||||
**How to Use Extension**
|
||||
|
||||
1. [Download the Extension](https://chromewebstore.google.com/detail/core-extension/cglndoindnhdbfcbijikibfjoholdjcc) from the Chrome Web Store.
|
||||
2. Login to [CORE dashboard](https://core.heysol.ai)
|
||||
- Navigate to Settings (bottom left)
|
||||
@ -108,13 +108,12 @@ Save conversations and content from ChatGPT, Grok, Gemini, Twitter, YouTube, blo
|
||||
|
||||
https://github.com/user-attachments/assets/6e629834-1b9d-4fe6-ae58-a9068986036a
|
||||
|
||||
|
||||
### 💬 **Chat with Memory**:
|
||||
|
||||
Ask questions like "What are my writing preferences?" with instant insights from your connected knowledge
|
||||
|
||||

|
||||
|
||||
|
||||
### ⚡ **Auto-Sync from Apps**:
|
||||
|
||||
Automatically capture relevant context from Linear, Slack, Notion, GitHub and other connected apps into your CORE memory
|
||||
@ -123,16 +122,12 @@ Automatically capture relevant context from Linear, Slack, Notion, GitHub and ot
|
||||
|
||||

|
||||
|
||||
|
||||
### 🔗 **MCP Integration Hub**:
|
||||
|
||||
Connect Linear, Slack, GitHub, Notion once to CORE—then use all their tools in Claude, Cursor, or any MCP client with a single URL
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## How CORE create memory
|
||||
|
||||
<img width="12885" height="3048" alt="memory-ingest-diagram" src="https://github.com/user-attachments/assets/c51679de-8260-4bee-bebf-aff32c6b8e13" />
|
||||
@ -146,7 +141,6 @@ CORE’s ingestion pipeline has four phases designed to capture evolving context
|
||||
|
||||
The Result: Instead of a flat database, CORE gives you a memory that grows and changes with you - preserving context, evolution, and ownership so agents can actually use it.
|
||||
|
||||
|
||||

|
||||
|
||||
## How CORE recalls from memory
|
||||
@ -183,7 +177,7 @@ CORE takes security seriously. We implement industry-standard security practices
|
||||
- **Data Encryption**: All data in transit (TLS 1.3) and at rest (AES-256)
|
||||
- **Authentication**: OAuth 2.0 and magic link authentication
|
||||
- **Access Control**: Workspace-based isolation and role-based permissions
|
||||
- **Vulnerability Reporting**: Please report security issues to harshith@tegon.ai
|
||||
- **Vulnerability Reporting**: Please report security issues to harshith@poozle.dev
|
||||
|
||||
For detailed security information, see our [Security Policy](SECURITY.md).
|
||||
|
||||
@ -216,12 +210,3 @@ Have questions or feedback? We're here to help:
|
||||
<a href="https://github.com/RedPlanetHQ/core/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=RedPlanetHQ/core" />
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
10
SECURITY.md
10
SECURITY.md
@ -19,9 +19,10 @@ We appreciate responsible disclosure of security vulnerabilities. If you discove
|
||||
|
||||
### How to Report
|
||||
|
||||
**📧 Email**: harshith@tegon.ai
|
||||
**📧 Email**: harshith@poozle.dev
|
||||
|
||||
Please include the following information in your report:
|
||||
|
||||
- A clear description of the vulnerability
|
||||
- Steps to reproduce the issue
|
||||
- Potential impact and severity assessment
|
||||
@ -119,7 +120,8 @@ Security updates will be:
|
||||
## Contact Information
|
||||
|
||||
For security-related inquiries:
|
||||
- **Security Team**: harshith@tegon.ai
|
||||
|
||||
- **Security Team**: harshith@poozle.dev
|
||||
- **General Support**: [Discord Community](https://discord.gg/YGUZcvDjUa)
|
||||
|
||||
## Bug Bounty Program
|
||||
@ -128,5 +130,5 @@ We are currently evaluating the implementation of a formal bug bounty program. I
|
||||
|
||||
---
|
||||
|
||||
*Last Updated: January 2025*
|
||||
*Version: 1.0*
|
||||
_Last Updated: January 2025_
|
||||
_Version: 1.0_
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import ReactMarkdown, {type Components } from "react-markdown";
|
||||
import ReactMarkdown, { type Components } from "react-markdown";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
const markdownComponents: Components = {
|
||||
@ -74,7 +74,7 @@ const markdownComponents: Components = {
|
||||
ol: ({ className, ...props }) => (
|
||||
<ol
|
||||
className={cn(
|
||||
"my-1 ml-5 list-decimal space-y-0 marker:text-gray-700 dark:marker:text-gray-400",
|
||||
"my-1 ml-5 flex list-decimal flex-col space-y-0 marker:text-gray-700 dark:marker:text-gray-400",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
85
packages/cli/.eslintrc.cjs
Normal file
85
packages/cli/.eslintrc.cjs
Normal file
@ -0,0 +1,85 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'prettier',
|
||||
'eslint:recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'next/core-web-vitals',
|
||||
'turbo',
|
||||
],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'prettier',
|
||||
'unused-imports',
|
||||
'notice',
|
||||
'import',
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
curly: 'warn',
|
||||
eqeqeq: 'error',
|
||||
'prettier/prettier': 'warn',
|
||||
'unused-imports/no-unused-imports': 'warn',
|
||||
'no-else-return': 'warn',
|
||||
'no-lonely-if': 'warn',
|
||||
'no-inner-declarations': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'no-useless-computed-key': 'warn',
|
||||
'no-useless-return': 'warn',
|
||||
'no-var': 'warn',
|
||||
'object-shorthand': ['warn', 'always'],
|
||||
'prefer-arrow-callback': 'warn',
|
||||
'prefer-const': 'warn',
|
||||
'prefer-destructuring': ['warn', { AssignmentExpression: { array: true } }],
|
||||
'prefer-object-spread': 'warn',
|
||||
'prefer-template': 'warn',
|
||||
'spaced-comment': ['warn', 'always', { markers: ['/'] }],
|
||||
yoda: 'warn',
|
||||
'import/order': [
|
||||
'warn',
|
||||
{
|
||||
'newlines-between': 'always',
|
||||
groups: [
|
||||
'type',
|
||||
'builtin',
|
||||
'external',
|
||||
'internal',
|
||||
['parent', 'sibling'],
|
||||
'index',
|
||||
],
|
||||
pathGroupsExcludedImportTypes: ['builtin'],
|
||||
alphabetize: {
|
||||
order:
|
||||
'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */,
|
||||
caseInsensitive: true /* ignore case. Options: [true, false] */,
|
||||
},
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/array-type': ['warn', { default: 'array-simple' }],
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'warn',
|
||||
{
|
||||
'ts-expect-error': 'allow-with-description',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/ban-types': 'warn',
|
||||
'@typescript-eslint/consistent-indexed-object-style': ['warn', 'record'],
|
||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
ignorePatterns: ['dist', 'node_modules'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['scripts/**/*'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
4
packages/cli/.prettierrc.json
Normal file
4
packages/cli/.prettierrc.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
0
packages/cli/README.md
Normal file
0
packages/cli/README.md
Normal file
95
packages/cli/package.json
Normal file
95
packages/cli/package.json
Normal file
@ -0,0 +1,95 @@
|
||||
{
|
||||
"name": "@redplanet/core",
|
||||
"version": "0.1.0",
|
||||
"description": "A Command-Line for project init in core",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/redplanethq/core.git",
|
||||
"directory": "packages/cli"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [
|
||||
"typescript"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"type": "module",
|
||||
"exports": "./dist/index.js",
|
||||
"bin": {
|
||||
"core": "./dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/degit": "^2.8.6",
|
||||
"@types/gradient-string": "^1.1.2",
|
||||
"@types/node": "18",
|
||||
"@types/object-hash": "^3.0.6",
|
||||
"@types/semver": "^7.3.13",
|
||||
"@types/ws": "^8.5.3",
|
||||
"eslint": "^8.56.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"open": "^10.0.3",
|
||||
"p-retry": "^6.2.0",
|
||||
"rimraf": "^5.0.7",
|
||||
"tsup": "^8.0.1",
|
||||
"type-fest": "^3.6.0",
|
||||
"@vercel/style-guide": "^5.2.0",
|
||||
"eslint-config-turbo": "^2.0.0",
|
||||
"eslint-config-next": "14.1.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-notice": "^0.9.10",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"eslint-plugin-only-warn": "^1.1.0",
|
||||
"@typescript-eslint/parser": "^7.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.check.json",
|
||||
"build": "pnpm run clean && pnpm run build:main",
|
||||
"build:main": "tsup",
|
||||
"dev:cli": "npm run clean && pnpm run dev:main",
|
||||
"dev:main": "tsup --watch",
|
||||
"clean": "rimraf dist",
|
||||
"start": "node dist/index.js",
|
||||
"lint": "eslint \"src/**/*.ts\" --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anatine/esbuild-decorators": "^0.2.19",
|
||||
"@clack/prompts": "^0.7.0",
|
||||
"axios": "^1.6.7",
|
||||
"chalk": "^5.2.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"cli-table3": "^0.6.3",
|
||||
"commander": "^9.4.1",
|
||||
"cpy": "^11.1.0",
|
||||
"degit": "^2.8.4",
|
||||
"dotenv": "^16.4.4",
|
||||
"esbuild": "^0.19.11",
|
||||
"execa": "^9.3.0",
|
||||
"glob": "^10.3.10",
|
||||
"gradient-string": "^2.0.2",
|
||||
"import-meta-resolve": "^4.0.0",
|
||||
"ink": "^4.4.1",
|
||||
"node-fetch": "^3.3.0",
|
||||
"semver": "^7.5.0",
|
||||
"std-env": "^3.7.0",
|
||||
"terminal-link": "^3.0.0",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.4.0",
|
||||
"url": "^0.11.1",
|
||||
"xdg-app-paths": "^8.3.0",
|
||||
"zod": "3.22.3",
|
||||
"zod-validation-error": "^1.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
70
packages/cli/src/cli/common.ts
Normal file
70
packages/cli/src/cli/common.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { outro } from '@clack/prompts';
|
||||
import { Command } from 'commander';
|
||||
import { z } from 'zod';
|
||||
import { fromZodError } from 'zod-validation-error';
|
||||
|
||||
import { chalkError } from '../utilities/cliOutput';
|
||||
import { logger } from '../utilities/logger';
|
||||
|
||||
export const CommonCommandOptions = z.object({
|
||||
apiUrl: z.string().optional(),
|
||||
logLevel: z
|
||||
.enum(['debug', 'info', 'log', 'warn', 'error', 'none'])
|
||||
.default('log'),
|
||||
skipTelemetry: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export type CommonCommandOptions = z.infer<typeof CommonCommandOptions>;
|
||||
|
||||
export function commonOptions(command: Command) {
|
||||
return command.option(
|
||||
'-l, --log-level <level>',
|
||||
'The CLI log level to use (debug, info, log, warn, error, none). This does not effect the log level of your tegon actions',
|
||||
'log',
|
||||
);
|
||||
}
|
||||
|
||||
export class SkipLoggingError extends Error {}
|
||||
export class SkipCommandError extends Error {}
|
||||
export class OutroCommandError extends SkipCommandError {}
|
||||
|
||||
export async function wrapCommandAction<T extends z.AnyZodObject, TResult>(
|
||||
name: string,
|
||||
schema: T,
|
||||
options: unknown,
|
||||
action: (opts: z.output<T>) => Promise<TResult>,
|
||||
): Promise<TResult> {
|
||||
try {
|
||||
const parsedOptions = schema.safeParse(options);
|
||||
|
||||
if (!parsedOptions.success) {
|
||||
throw new Error(fromZodError(parsedOptions.error).toString());
|
||||
}
|
||||
|
||||
logger.loggerLevel = parsedOptions.data.logLevel;
|
||||
|
||||
logger.debug(`Running "${name}" with the following options`, {
|
||||
options,
|
||||
});
|
||||
|
||||
const result = await action(parsedOptions.data);
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
if (e instanceof SkipLoggingError) {
|
||||
logger.log(
|
||||
`${chalkError('X Error:')} ${e instanceof Error ? e.message : String(e)}`,
|
||||
);
|
||||
} else if (e instanceof OutroCommandError) {
|
||||
outro('Operation cancelled');
|
||||
} else if (e instanceof SkipCommandError) {
|
||||
// do nothing
|
||||
} else {
|
||||
logger.log(
|
||||
`${chalkError('X Error:')} ${e instanceof Error ? e.message : String(e)}`,
|
||||
);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
14
packages/cli/src/cli/index.ts
Normal file
14
packages/cli/src/cli/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Command } from 'commander';
|
||||
|
||||
import { configureInitCommand } from '../commands/init';
|
||||
import { COMMAND_NAME } from '../consts';
|
||||
import { getVersion } from '../utilities/getVersion';
|
||||
|
||||
export const program = new Command();
|
||||
|
||||
program
|
||||
.name(COMMAND_NAME)
|
||||
.description('Cli to run core')
|
||||
.version(getVersion(), '-v, --version', 'Display the version number');
|
||||
|
||||
configureInitCommand(program);
|
||||
29
packages/cli/src/commands/init.ts
Normal file
29
packages/cli/src/commands/init.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'path';
|
||||
|
||||
import { spinner, intro, outro } from '@clack/prompts';
|
||||
import { Command } from 'commander';
|
||||
import degit from 'degit';
|
||||
|
||||
import { commonOptions } from '../cli/common';
|
||||
import { getVersion } from '../utilities/getVersion';
|
||||
import { printInitialBanner } from '../utilities/initialBanner';
|
||||
|
||||
export function configureInitCommand(program: Command) {
|
||||
return commonOptions(
|
||||
program
|
||||
.command('init')
|
||||
.description('Init a tegon action')
|
||||
.option(
|
||||
'-a, --action <name>',
|
||||
'Name of the action folder to initialize',
|
||||
'base',
|
||||
),
|
||||
)
|
||||
.version(getVersion(), '-v, --version', 'Display the version number')
|
||||
.action(async (options) => {
|
||||
await printInitialBanner();
|
||||
|
||||
const { action } = options;
|
||||
});
|
||||
}
|
||||
11
packages/cli/src/consts.ts
Normal file
11
packages/cli/src/consts.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
// With the move to TSUP as a build tool, this keeps path routes in other files (installers, loaders, etc) in check more easily.
|
||||
// Path is in relation to a single index.js file inside ./dist
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const distPath = path.dirname(__filename);
|
||||
|
||||
export const PKG_ROOT = path.join(distPath, '../');
|
||||
export const COMMAND_NAME = 'core';
|
||||
export const CONFIG_FILE = 'config.json';
|
||||
20
packages/cli/src/index.ts
Normal file
20
packages/cli/src/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { program } from './cli/index';
|
||||
import { logger } from './utilities/logger';
|
||||
|
||||
const main = async () => {
|
||||
await program.parseAsync();
|
||||
};
|
||||
|
||||
main().catch((err) => {
|
||||
if (err instanceof Error) {
|
||||
logger.error(err);
|
||||
} else {
|
||||
logger.error(
|
||||
'An unknown error has occurred. Please open an issue on github with the below:',
|
||||
);
|
||||
logger.error(err);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
14
packages/cli/src/utilities/axios.ts
Normal file
14
packages/cli/src/utilities/axios.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// Intercept axios requests and add token to the request
|
||||
export function interceptAxios(token: string) {
|
||||
axios.interceptors.request.use((axiosConfig) => {
|
||||
if (!axiosConfig.headers.Authorization) {
|
||||
if (token) {
|
||||
axiosConfig.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
|
||||
return axiosConfig;
|
||||
});
|
||||
}
|
||||
124
packages/cli/src/utilities/cliOutput.ts
Normal file
124
packages/cli/src/utilities/cliOutput.ts
Normal file
@ -0,0 +1,124 @@
|
||||
import { log } from '@clack/prompts';
|
||||
import chalk from 'chalk';
|
||||
import terminalLink, { Options as TerminalLinkOptions } from 'terminal-link';
|
||||
|
||||
export const green = '#4FFF54';
|
||||
export const purple = '#735BF3';
|
||||
|
||||
export function chalkGreen(text: string) {
|
||||
return chalk.hex(green)(text);
|
||||
}
|
||||
|
||||
export function chalkPurple(text: string) {
|
||||
return chalk.hex(purple)(text);
|
||||
}
|
||||
|
||||
export function chalkGrey(text: string) {
|
||||
return chalk.hex('#878C99')(text);
|
||||
}
|
||||
|
||||
export function chalkError(text: string) {
|
||||
return chalk.hex('#E11D48')(text);
|
||||
}
|
||||
|
||||
export function chalkWarning(text: string) {
|
||||
return chalk.yellow(text);
|
||||
}
|
||||
|
||||
export function chalkSuccess(text: string) {
|
||||
return chalk.hex('#28BF5C')(text);
|
||||
}
|
||||
|
||||
export function chalkLink(text: string) {
|
||||
return chalk.underline.hex('#D7D9DD')(text);
|
||||
}
|
||||
|
||||
export function chalkWorker(text: string) {
|
||||
return chalk.hex('#FFFF89')(text);
|
||||
}
|
||||
|
||||
export function chalkTask(text: string) {
|
||||
return chalk.hex('#60A5FA')(text);
|
||||
}
|
||||
|
||||
export function chalkRun(text: string) {
|
||||
return chalk.hex('#A78BFA')(text);
|
||||
}
|
||||
|
||||
export function logo() {
|
||||
return `${chalk.hex(purple).bold('Core')}`;
|
||||
}
|
||||
|
||||
// Mar 27 09:17:25.653
|
||||
export function prettyPrintDate(date: Date = new Date()) {
|
||||
let formattedDate = new Intl.DateTimeFormat('en-US', {
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
}).format(date);
|
||||
|
||||
// Append milliseconds
|
||||
formattedDate += `.${`00${date.getMilliseconds()}`.slice(-3)}`;
|
||||
|
||||
return formattedDate;
|
||||
}
|
||||
|
||||
export function prettyError(header: string, body?: string, footer?: string) {
|
||||
const prefix = 'Error: ';
|
||||
const indent = Array(prefix.length).fill(' ').join('');
|
||||
const spacing = '\n\n';
|
||||
|
||||
const prettyPrefix = chalkError(prefix);
|
||||
|
||||
const withIndents = (text?: string) =>
|
||||
text
|
||||
?.split('\n')
|
||||
.map((line) => `${indent}${line}`)
|
||||
.join('\n');
|
||||
|
||||
const prettyBody = withIndents(body);
|
||||
const prettyFooter = withIndents(footer);
|
||||
|
||||
log.error(
|
||||
`${prettyPrefix}${header}${prettyBody ? `${spacing}${prettyBody}` : ''}${
|
||||
prettyFooter ? `${spacing}${prettyFooter}` : ''
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function prettyWarning(header: string, body?: string, footer?: string) {
|
||||
const prefix = 'Warning: ';
|
||||
const indent = Array(prefix.length).fill(' ').join('');
|
||||
const spacing = '\n\n';
|
||||
|
||||
const prettyPrefix = chalkWarning(prefix);
|
||||
|
||||
const withIndents = (text?: string) =>
|
||||
text
|
||||
?.split('\n')
|
||||
.map((line) => `${indent}${line}`)
|
||||
.join('\n');
|
||||
|
||||
const prettyBody = withIndents(body);
|
||||
const prettyFooter = withIndents(footer);
|
||||
|
||||
log.warn(
|
||||
`${prettyPrefix}${header}${prettyBody ? `${spacing}${prettyBody}` : ''}${
|
||||
prettyFooter ? `${spacing}${prettyFooter}` : ''
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function cliLink(
|
||||
text: string,
|
||||
url: string,
|
||||
options?: TerminalLinkOptions,
|
||||
) {
|
||||
return terminalLink(text, url, {
|
||||
fallback: (text, url) => `${text} ${url}`,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
93
packages/cli/src/utilities/configFiles.ts
Normal file
93
packages/cli/src/utilities/configFiles.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { mkdirSync, writeFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import xdgAppPaths from 'xdg-app-paths';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { readJSONFileSync } from './fileSystem.js';
|
||||
import { logger } from './logger.js';
|
||||
|
||||
function getGlobalConfigFolderPath() {
|
||||
const configDir = xdgAppPaths('core').config();
|
||||
|
||||
return configDir;
|
||||
}
|
||||
|
||||
// auth config file
|
||||
export const UserAuthConfigSchema = z.object({
|
||||
accessToken: z.string().optional(),
|
||||
apiUrl: z.string().optional(),
|
||||
triggerUrl: z.string().optional(),
|
||||
workspaceId: z.string().optional(),
|
||||
});
|
||||
|
||||
export type UserAuthConfig = z.infer<typeof UserAuthConfigSchema>;
|
||||
|
||||
const UserAuthConfigFileSchema = z.record(UserAuthConfigSchema);
|
||||
|
||||
type UserAuthConfigFile = z.infer<typeof UserAuthConfigFileSchema>;
|
||||
|
||||
function getAuthConfigFilePath() {
|
||||
return path.join(getGlobalConfigFolderPath(), 'default.json');
|
||||
}
|
||||
|
||||
export function writeAuthConfigProfile(
|
||||
config: UserAuthConfig,
|
||||
profile: string = 'default',
|
||||
) {
|
||||
const existingConfig = readAuthConfigFile() || {};
|
||||
|
||||
existingConfig[profile] = config;
|
||||
|
||||
writeAuthConfigFile(existingConfig);
|
||||
}
|
||||
|
||||
export function readAuthConfigProfile(
|
||||
profile: string = 'default',
|
||||
): UserAuthConfig | undefined {
|
||||
try {
|
||||
const authConfigFilePath = getAuthConfigFilePath();
|
||||
|
||||
logger.debug(`Reading auth config file`, { authConfigFilePath });
|
||||
|
||||
const json = readJSONFileSync(authConfigFilePath);
|
||||
const parsed = UserAuthConfigFileSchema.parse(json);
|
||||
return parsed[profile];
|
||||
} catch (error) {
|
||||
logger.debug(`Error reading auth config file: ${error}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteAuthConfigProfile(profile: string = 'default') {
|
||||
const existingConfig = readAuthConfigFile() || {};
|
||||
|
||||
delete existingConfig[profile];
|
||||
|
||||
writeAuthConfigFile(existingConfig);
|
||||
}
|
||||
|
||||
export function readAuthConfigFile(): UserAuthConfigFile | undefined {
|
||||
try {
|
||||
const authConfigFilePath = getAuthConfigFilePath();
|
||||
|
||||
logger.debug(`Reading auth config file`, { authConfigFilePath });
|
||||
|
||||
const json = readJSONFileSync(authConfigFilePath);
|
||||
const parsed = UserAuthConfigFileSchema.parse(json);
|
||||
return parsed;
|
||||
} catch (error) {
|
||||
logger.debug(`Error reading auth config file: ${error}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function writeAuthConfigFile(config: UserAuthConfigFile) {
|
||||
const authConfigFilePath = getAuthConfigFilePath();
|
||||
mkdirSync(path.dirname(authConfigFilePath), {
|
||||
recursive: true,
|
||||
});
|
||||
writeFileSync(path.join(authConfigFilePath), JSON.stringify(config), {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
}
|
||||
84
packages/cli/src/utilities/configValidation.ts
Normal file
84
packages/cli/src/utilities/configValidation.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'path';
|
||||
|
||||
import { log } from '@clack/prompts';
|
||||
|
||||
import { readJSONFileSync } from './fileSystem';
|
||||
import { ConfigMap } from '../api/client';
|
||||
|
||||
export const activeProcesses: any[] = []; // Store active processes for cleanup
|
||||
|
||||
// Validate and export the found config files
|
||||
export async function validateAndExportConfigs(
|
||||
configPaths: string[],
|
||||
cwd: string,
|
||||
): Promise<ConfigMap[]> {
|
||||
const validConfigs: ConfigMap[] = [];
|
||||
|
||||
for (const configPath of configPaths) {
|
||||
const configMap = await validateAndExportConfig(cwd, configPath);
|
||||
|
||||
if (configMap) {
|
||||
validConfigs.push(configMap);
|
||||
}
|
||||
}
|
||||
|
||||
return validConfigs;
|
||||
}
|
||||
|
||||
export async function validateAndExportConfig(
|
||||
cwd: string,
|
||||
configPath: string,
|
||||
): Promise<ConfigMap | undefined> {
|
||||
const dir = path.dirname(configPath);
|
||||
const indexPath = path.join(dir, 'index.ts');
|
||||
|
||||
if (!fs.existsSync(indexPath)) {
|
||||
log.error(`index.ts not found at ${indexPath}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
log.info(`Found index.ts at ${indexPath}`);
|
||||
|
||||
const config = readJSONFileSync(configPath);
|
||||
const relativeDir = `./${path.relative(cwd, dir)}`;
|
||||
|
||||
return { path: relativeDir, config };
|
||||
}
|
||||
|
||||
// Utility function to check for all config.json files
|
||||
export const checkConfigFiles = (dir: string): string[] => {
|
||||
const foundConfigs: string[] = [];
|
||||
|
||||
// Check for config.json in the root directory
|
||||
const rootConfigPath = path.join(dir, 'config.json');
|
||||
if (fs.existsSync(rootConfigPath)) {
|
||||
foundConfigs.push(rootConfigPath);
|
||||
}
|
||||
|
||||
// Check each subdirectory one level down for config.json
|
||||
const subDirs = fs
|
||||
.readdirSync(dir, { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.map((dirent) => path.join(dir, dirent.name));
|
||||
|
||||
for (const subDir of subDirs) {
|
||||
const subConfigPath = path.join(subDir, 'config.json');
|
||||
if (fs.existsSync(subConfigPath)) {
|
||||
foundConfigs.push(subConfigPath);
|
||||
}
|
||||
}
|
||||
|
||||
return foundConfigs;
|
||||
};
|
||||
|
||||
export const checkIfConfigExist = (dir: string) => {
|
||||
// Check for config.json in the root directory
|
||||
const rootConfigPath = path.join(dir, 'config.json');
|
||||
|
||||
if (fs.existsSync(rootConfigPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
113
packages/cli/src/utilities/fileSystem.ts
Normal file
113
packages/cli/src/utilities/fileSystem.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import fsSync from 'fs';
|
||||
import fsModule, { writeFile } from 'fs/promises';
|
||||
import fs from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import pathModule from 'node:path';
|
||||
|
||||
import { log } from '@clack/prompts';
|
||||
|
||||
// Creates a file at the given path, if the directory doesn't exist it will be created
|
||||
export async function createFile(
|
||||
path: string,
|
||||
contents: string,
|
||||
): Promise<string> {
|
||||
await fsModule.mkdir(pathModule.dirname(path), { recursive: true });
|
||||
await fsModule.writeFile(path, contents);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
export function isDirectory(configPath: string) {
|
||||
try {
|
||||
return fs.statSync(configPath).isDirectory();
|
||||
} catch (error) {
|
||||
// ignore error
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function pathExists(path: string): Promise<boolean> {
|
||||
return fsSync.existsSync(path);
|
||||
}
|
||||
|
||||
export async function someFileExists(
|
||||
directory: string,
|
||||
filenames: string[],
|
||||
): Promise<boolean> {
|
||||
for (let index = 0; index < filenames.length; index++) {
|
||||
const filename = filenames[index];
|
||||
if (!filename) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const path = pathModule.join(directory, filename);
|
||||
if (await pathExists(path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function removeFile(path: string) {
|
||||
await fsModule.unlink(path);
|
||||
}
|
||||
|
||||
export async function readFile(path: string) {
|
||||
return await fsModule.readFile(path, 'utf8');
|
||||
}
|
||||
|
||||
export async function readJSONFile(path: string) {
|
||||
const fileContents = await fsModule.readFile(path, 'utf8');
|
||||
|
||||
return JSON.parse(fileContents);
|
||||
}
|
||||
|
||||
export async function safeFeadJSONFile(path: string) {
|
||||
try {
|
||||
const fileExists = await pathExists(path);
|
||||
|
||||
if (!fileExists) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileContents = await readFile(path);
|
||||
|
||||
return JSON.parse(fileContents);
|
||||
} catch (e: any) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function writeJSONFile(path: string, json: any, pretty = false) {
|
||||
await writeFile(
|
||||
path,
|
||||
JSON.stringify(json, undefined, pretty ? 2 : undefined),
|
||||
'utf8',
|
||||
);
|
||||
}
|
||||
|
||||
export function readJSONFileSync(path: string) {
|
||||
const fileContents = fsSync.readFileSync(path, 'utf8');
|
||||
|
||||
return JSON.parse(fileContents);
|
||||
}
|
||||
|
||||
export function safeDeleteFileSync(path: string) {
|
||||
try {
|
||||
fs.unlinkSync(path);
|
||||
} catch (error) {
|
||||
// ignore error
|
||||
}
|
||||
}
|
||||
|
||||
// Create a temporary directory within the OS's temp directory
|
||||
export async function createTempDir(): Promise<string> {
|
||||
// Generate a unique temp directory path
|
||||
const tempDirPath: string = pathModule.join(tmpdir(), 'core-temp-');
|
||||
|
||||
// Create the temp directory synchronously and return the path
|
||||
const directory = await fsModule.mkdtemp(tempDirPath);
|
||||
|
||||
return directory;
|
||||
}
|
||||
68
packages/cli/src/utilities/getEnviromentVariableFactory.ts
Normal file
68
packages/cli/src/utilities/getEnviromentVariableFactory.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { logger } from './logger';
|
||||
|
||||
type VariableNames = 'CORE_LOG_LEVEL';
|
||||
|
||||
type DeprecatedNames = '';
|
||||
|
||||
/**
|
||||
* Create a function used to access an environment variable.
|
||||
*
|
||||
* This is not memoized to allow us to change the value at runtime, such as in testing.
|
||||
* A warning is shown if the client is using a deprecated version - but only once.
|
||||
*/
|
||||
export function getEnvironmentVariableFactory({
|
||||
variableName,
|
||||
deprecatedName,
|
||||
}: {
|
||||
variableName: VariableNames;
|
||||
deprecatedName?: DeprecatedNames;
|
||||
}): () => string | undefined;
|
||||
|
||||
/**
|
||||
* Create a function used to access an environment variable, with a default value.
|
||||
*
|
||||
* This is not memoized to allow us to change the value at runtime, such as in testing.
|
||||
* A warning is shown if the client is using a deprecated version - but only once.
|
||||
*/
|
||||
export function getEnvironmentVariableFactory({
|
||||
variableName,
|
||||
deprecatedName,
|
||||
defaultValue,
|
||||
}: {
|
||||
variableName: VariableNames;
|
||||
deprecatedName?: DeprecatedNames;
|
||||
defaultValue: () => string;
|
||||
}): () => string;
|
||||
|
||||
/**
|
||||
* Create a function used to access an environment variable.
|
||||
*
|
||||
* This is not memoized to allow us to change the value at runtime, such as in testing.
|
||||
* A warning is shown if the client is using a deprecated version - but only once.
|
||||
*/
|
||||
export function getEnvironmentVariableFactory({
|
||||
variableName,
|
||||
deprecatedName,
|
||||
defaultValue,
|
||||
}: {
|
||||
variableName: VariableNames;
|
||||
deprecatedName?: DeprecatedNames;
|
||||
defaultValue?: () => string;
|
||||
}): () => string | undefined {
|
||||
let hasWarned = false;
|
||||
return () => {
|
||||
if (process.env[variableName]) {
|
||||
return process.env[variableName];
|
||||
} else if (deprecatedName && process.env[deprecatedName]) {
|
||||
if (!hasWarned) {
|
||||
// Only show the warning once.
|
||||
hasWarned = true;
|
||||
logger.warn(
|
||||
`Using "${deprecatedName}" environment variable. This is deprecated. Please use "${variableName}", instead.`,
|
||||
);
|
||||
}
|
||||
return process.env[deprecatedName];
|
||||
}
|
||||
return defaultValue?.();
|
||||
};
|
||||
}
|
||||
14
packages/cli/src/utilities/getVersion.ts
Normal file
14
packages/cli/src/utilities/getVersion.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import path from 'path';
|
||||
|
||||
import { type PackageJson } from 'type-fest';
|
||||
|
||||
import { readJSONFileSync } from './fileSystem';
|
||||
import { PKG_ROOT } from '../consts';
|
||||
|
||||
export function getVersion() {
|
||||
const packageJsonPath = path.join(PKG_ROOT, 'package.json');
|
||||
|
||||
const packageJsonContent = readJSONFileSync(packageJsonPath) as PackageJson;
|
||||
|
||||
return packageJsonContent.version ?? '1.0.0';
|
||||
}
|
||||
30
packages/cli/src/utilities/initialBanner.ts
Normal file
30
packages/cli/src/utilities/initialBanner.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { chalkGrey, chalkRun, chalkTask, chalkWorker, logo } from './cliOutput';
|
||||
import { getVersion } from './getVersion';
|
||||
import { logger } from './logger';
|
||||
|
||||
export async function printInitialBanner() {
|
||||
const cliVersion = getVersion();
|
||||
const text = `\n${logo()} ${chalkGrey(`(${cliVersion})`)}\n`;
|
||||
|
||||
logger.info(text);
|
||||
}
|
||||
|
||||
export async function printStandloneInitialBanner() {
|
||||
const cliVersion = getVersion();
|
||||
|
||||
logger.log(`\n${logo()} ${chalkGrey(`(${cliVersion})`)}`);
|
||||
logger.log(`${chalkGrey('-'.repeat(54))}`);
|
||||
}
|
||||
|
||||
export function printDevBanner(printTopBorder = true) {
|
||||
if (printTopBorder) {
|
||||
logger.log(chalkGrey('-'.repeat(54)));
|
||||
}
|
||||
|
||||
logger.log(
|
||||
`${chalkGrey('Key:')} ${chalkWorker('Version')} ${chalkGrey('|')} ${chalkTask(
|
||||
'Task',
|
||||
)} ${chalkGrey('|')} ${chalkRun('Run')}`,
|
||||
);
|
||||
logger.log(chalkGrey('-'.repeat(54)));
|
||||
}
|
||||
147
packages/cli/src/utilities/logger.ts
Normal file
147
packages/cli/src/utilities/logger.ts
Normal file
@ -0,0 +1,147 @@
|
||||
// This is a copy of the logger utility from the wrangler repo: https://github.com/cloudflare/workers-sdk/blob/main/packages/wrangler/src/logger.ts
|
||||
|
||||
import type { Message } from 'esbuild';
|
||||
|
||||
import { format } from 'node:util';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import CLITable from 'cli-table3';
|
||||
import { formatMessagesSync } from 'esbuild';
|
||||
|
||||
import { getEnvironmentVariableFactory } from './getEnviromentVariableFactory';
|
||||
export const LOGGER_LEVELS = {
|
||||
none: -1,
|
||||
error: 0,
|
||||
warn: 1,
|
||||
info: 2,
|
||||
log: 3,
|
||||
debug: 4,
|
||||
} as const;
|
||||
|
||||
export type LoggerLevel = keyof typeof LOGGER_LEVELS;
|
||||
|
||||
/** A map from LOGGER_LEVEL to the error `kind` needed by `formatMessagesSync()`. */
|
||||
const LOGGER_LEVEL_FORMAT_TYPE_MAP = {
|
||||
error: 'error',
|
||||
warn: 'warning',
|
||||
info: undefined,
|
||||
log: undefined,
|
||||
debug: undefined,
|
||||
} as const;
|
||||
|
||||
const getLogLevelFromEnv = getEnvironmentVariableFactory({
|
||||
variableName: 'CORE_LOG_LEVEL',
|
||||
});
|
||||
|
||||
function getLoggerLevel(): LoggerLevel {
|
||||
const fromEnv = getLogLevelFromEnv()?.toLowerCase();
|
||||
if (fromEnv !== undefined) {
|
||||
if (fromEnv in LOGGER_LEVELS) {
|
||||
return fromEnv as LoggerLevel;
|
||||
}
|
||||
const expected = Object.keys(LOGGER_LEVELS)
|
||||
.map((level) => `"${level}"`)
|
||||
.join(' | ');
|
||||
console.warn(
|
||||
`Unrecognised WRANGLER_LOG value ${JSON.stringify(
|
||||
fromEnv,
|
||||
)}, expected ${expected}, defaulting to "log"...`,
|
||||
);
|
||||
}
|
||||
return 'log';
|
||||
}
|
||||
|
||||
export type TableRow<Keys extends string> = Record<Keys, string>;
|
||||
|
||||
export class Logger {
|
||||
constructor() {}
|
||||
|
||||
loggerLevel = getLoggerLevel();
|
||||
columns = process.stdout.columns;
|
||||
|
||||
debug = (...args: unknown[]) => this.doLog('debug', args);
|
||||
ignore = () => {};
|
||||
debugWithSanitization = (label: string, ...args: unknown[]) => {
|
||||
this.doLog('debug', [label, ...args]);
|
||||
};
|
||||
info = (...args: unknown[]) => this.doLog('info', args);
|
||||
log = (...args: unknown[]) => this.doLog('log', args);
|
||||
warn = (...args: unknown[]) => this.doLog('warn', args);
|
||||
error = (...args: unknown[]) => this.doLog('error', args);
|
||||
table<Keys extends string>(
|
||||
data: Array<TableRow<Keys>>,
|
||||
level?: Exclude<LoggerLevel, 'none'>,
|
||||
) {
|
||||
const keys: Keys[] =
|
||||
data.length === 0 ? [] : (Object.keys(data[0]!) as Keys[]);
|
||||
const t = new CLITable({
|
||||
head: keys,
|
||||
style: {
|
||||
head: chalk.level ? ['blue'] : [],
|
||||
border: chalk.level ? ['gray'] : [],
|
||||
},
|
||||
});
|
||||
t.push(...data.map((row) => keys.map((k) => row[k])));
|
||||
return this.doLog(level ?? 'log', [t.toString()]);
|
||||
}
|
||||
|
||||
private doLog(messageLevel: Exclude<LoggerLevel, 'none'>, args: unknown[]) {
|
||||
const message = this.formatMessage(messageLevel, format(...args));
|
||||
|
||||
// only send logs to the terminal if their level is at least the configured log-level
|
||||
if (LOGGER_LEVELS[this.loggerLevel] >= LOGGER_LEVELS[messageLevel]) {
|
||||
console[messageLevel](message);
|
||||
}
|
||||
}
|
||||
|
||||
private formatMessage(
|
||||
level: Exclude<LoggerLevel, 'none'>,
|
||||
message: string,
|
||||
): string {
|
||||
const kind = LOGGER_LEVEL_FORMAT_TYPE_MAP[level];
|
||||
if (kind) {
|
||||
// Format the message using the esbuild formatter.
|
||||
// The first line of the message is the main `text`,
|
||||
// subsequent lines are put into the `notes`.
|
||||
const [firstLine, ...otherLines] = message.split('\n');
|
||||
const notes =
|
||||
otherLines.length > 0
|
||||
? otherLines.map((text) => ({ text }))
|
||||
: undefined;
|
||||
return formatMessagesSync([{ text: firstLine, notes }], {
|
||||
color: true,
|
||||
kind,
|
||||
terminalWidth: this.columns,
|
||||
})[0]!;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A drop-in replacement for `console` for outputting logging messages.
|
||||
*
|
||||
* Errors and Warnings will get additional formatting to highlight them to the user.
|
||||
* You can also set a `logger.loggerLevel` value to one of "debug", "log", "warn" or "error",
|
||||
* to filter out logging messages.
|
||||
*/
|
||||
export const logger = new Logger();
|
||||
|
||||
export function logBuildWarnings(warnings: Message[]) {
|
||||
const logs = formatMessagesSync(warnings, { kind: 'warning', color: true });
|
||||
for (const log of logs) {
|
||||
console.warn(log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs all errors/warnings associated with an esbuild BuildFailure in the same
|
||||
* style esbuild would.
|
||||
*/
|
||||
export function logBuildFailure(errors: Message[], warnings: Message[]) {
|
||||
const logs = formatMessagesSync(errors, { kind: 'error', color: true });
|
||||
for (const log of logs) {
|
||||
console.error(log);
|
||||
}
|
||||
logBuildWarnings(warnings);
|
||||
}
|
||||
33
packages/cli/tsconfig.json
Normal file
33
packages/cli/tsconfig.json
Normal file
@ -0,0 +1,33 @@
|
||||
// See: https://www.totaltypescript.com/tsconfig-cheat-sheet
|
||||
{
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"./src/**/*.tsx",
|
||||
"./e2e/**/*.ts",
|
||||
"./types.d.ts",
|
||||
"./tsup.config.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "es2022",
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleDetection": "force",
|
||||
"isolatedModules": true,
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
/* Building for a monorepo */
|
||||
"declaration": true,
|
||||
"composite": false,
|
||||
"sourceMap": true,
|
||||
"declarationMap": true,
|
||||
/* We're building with tsup */
|
||||
"moduleResolution": "Bundler",
|
||||
"module": "ESNext",
|
||||
"noEmit": true,
|
||||
"lib": ["es2022", "DOM"],
|
||||
"types": ["node"]
|
||||
},
|
||||
"exclude": ["node_modules", "./e2e/fixtures"]
|
||||
}
|
||||
26
packages/cli/tsup.config.ts
Normal file
26
packages/cli/tsup.config.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
const isDev = process.env.npm_lifecycle_event === 'dev:main'; // This must match the npm script name
|
||||
|
||||
export default defineConfig({
|
||||
clean: false,
|
||||
tsconfig: 'tsconfig.json',
|
||||
dts: true,
|
||||
splitting: false,
|
||||
entry: ['src/index.ts'],
|
||||
format: ['esm'],
|
||||
minify: false,
|
||||
metafile: false,
|
||||
sourcemap: true,
|
||||
target: 'esnext',
|
||||
outDir: 'dist',
|
||||
async onSuccess() {
|
||||
if (isDev) {
|
||||
console.debug('Running onSuccess() in dev');
|
||||
// exec: node dist/index.js
|
||||
}
|
||||
},
|
||||
banner: {
|
||||
js: "import { createRequire as createRequireFromMetaUrl } from 'node:module';const require = createRequireFromMetaUrl(import.meta.url);",
|
||||
},
|
||||
});
|
||||
14
packages/cli/types.d.ts
vendored
Normal file
14
packages/cli/types.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
declare module 'terminal-link' {
|
||||
export interface Options {
|
||||
fallback?: ((text: string, url: string) => string) | boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated The default fallback is broken in some terminals. Please use `cliLink` instead.
|
||||
*/
|
||||
export default function terminalLink(
|
||||
text: string,
|
||||
url: string,
|
||||
options?: Options,
|
||||
): string;
|
||||
}
|
||||
1368
pnpm-lock.yaml
generated
1368
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user