Implemented opencode plugins
This commit is contained in:
parent
f6ba8dac45
commit
edbd384432
70
.opencode/plugin/dev-server-hmr.ts
Normal file
70
.opencode/plugin/dev-server-hmr.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import type { Plugin } from "@opencode-ai/plugin";
|
||||||
|
import { Axios } from "axios";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const { vmOrchestrationStatusUpdateUrl } = z
|
||||||
|
.object({
|
||||||
|
vmOrchestrationStatusUpdateUrl: z.string(),
|
||||||
|
})
|
||||||
|
.parse({
|
||||||
|
vmOrchestrationStatusUpdateUrl:
|
||||||
|
process.env.TAYLORDB_VM_ORCHESTRATION_STATUS_UPDATE_URL,
|
||||||
|
});
|
||||||
|
|
||||||
|
const axios = new Axios({
|
||||||
|
baseURL: vmOrchestrationStatusUpdateUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateAppStatus = async (status: "Errored" | "Active" | "Pending") => {
|
||||||
|
await axios.put("/", {
|
||||||
|
status,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DevServerHMRPlugin: Plugin = async ({ client, $ }) => {
|
||||||
|
return {
|
||||||
|
event: async ({ event }) => {
|
||||||
|
if (event.type !== "session.idle") return;
|
||||||
|
|
||||||
|
const result = await $`pnpm build`.catch((error) => error);
|
||||||
|
|
||||||
|
if (result.exitCode !== 0) {
|
||||||
|
if (!client.session["tries"]) {
|
||||||
|
client.session["tries"] = 0;
|
||||||
|
} else {
|
||||||
|
client.session["tries"]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.session["tries"] > 3) {
|
||||||
|
await updateAppStatus("Errored");
|
||||||
|
|
||||||
|
console.log("CHANGE STATUS TO ERRORED");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.session.prompt({
|
||||||
|
path: { id: event.properties.sessionID },
|
||||||
|
body: {
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `While building the project, the following error occurred:\n\n${result.stderr.toString()}\n\nPlease fix the error and try again.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateAppStatus("Active");
|
||||||
|
|
||||||
|
console.log("CHANGE STATUS TO ACTIVE");
|
||||||
|
},
|
||||||
|
|
||||||
|
"chat.message": async (input, output) => {
|
||||||
|
await updateAppStatus("Pending");
|
||||||
|
|
||||||
|
console.log("CHANGE STATUS TO PENDING");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
26
.opencode/plugin/file-protection.ts
Normal file
26
.opencode/plugin/file-protection.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import type { Plugin } from "@opencode-ai/plugin";
|
||||||
|
import micromatch from "micromatch";
|
||||||
|
|
||||||
|
const uneditableFiles = [
|
||||||
|
".env",
|
||||||
|
".env.local",
|
||||||
|
".env.development",
|
||||||
|
".env.production",
|
||||||
|
"src/lib/*.ts",
|
||||||
|
"opencode.json",
|
||||||
|
];
|
||||||
|
|
||||||
|
export const FileProtectionPlugin: Plugin = async ({ client, $ }) => {
|
||||||
|
return {
|
||||||
|
"tool.execute.before": async (input, output) => {
|
||||||
|
if (
|
||||||
|
input.tool === "edit" &&
|
||||||
|
uneditableFiles.some((pattern) =>
|
||||||
|
micromatch.isMatch(output.args.filePath, pattern)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error(`Do not edit ${output.args.filePath} files`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
410
.opencode/types/taylordb.types.ts
Normal file
410
.opencode/types/taylordb.types.ts
Normal file
|
|
@ -0,0 +1,410 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2025 TaylorDB
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface FileInformation {
|
||||||
|
fieldname: string;
|
||||||
|
originalname: string;
|
||||||
|
encoding: string;
|
||||||
|
mimetype: string;
|
||||||
|
destination: string;
|
||||||
|
filename: string;
|
||||||
|
path: string;
|
||||||
|
size: number;
|
||||||
|
format: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UploadResponse {
|
||||||
|
collectionName: string;
|
||||||
|
fileInformation: FileInformation;
|
||||||
|
metadata: {
|
||||||
|
thumbnails: any[];
|
||||||
|
clips: any[];
|
||||||
|
};
|
||||||
|
baseId: string;
|
||||||
|
storageAdaptor: string;
|
||||||
|
_id: string;
|
||||||
|
__v: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AttachmentColumnValue {
|
||||||
|
url: string;
|
||||||
|
fileType: string;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Attachment {
|
||||||
|
public readonly collectionName: string;
|
||||||
|
public readonly fileInformation: FileInformation;
|
||||||
|
public readonly metadata: { thumbnails: any[]; clips: any[] };
|
||||||
|
public readonly baseId: string;
|
||||||
|
public readonly storageAdaptor: string;
|
||||||
|
public readonly _id: string;
|
||||||
|
|
||||||
|
constructor(data: UploadResponse) {
|
||||||
|
this.collectionName = data.collectionName;
|
||||||
|
this.fileInformation = data.fileInformation;
|
||||||
|
this.metadata = data.metadata;
|
||||||
|
this.baseId = data.baseId;
|
||||||
|
this.storageAdaptor = data.storageAdaptor;
|
||||||
|
this._id = data._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
toColumnValue(): AttachmentColumnValue {
|
||||||
|
return {
|
||||||
|
url: this.fileInformation.path,
|
||||||
|
fileType: this.fileInformation.mimetype,
|
||||||
|
size: this.fileInformation.size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IsWithinOperatorValue =
|
||||||
|
| "pastWeek"
|
||||||
|
| "pastMonth"
|
||||||
|
| "pastYear"
|
||||||
|
| "nextWeek"
|
||||||
|
| "nextMonth"
|
||||||
|
| "nextYear"
|
||||||
|
| "daysFromNow"
|
||||||
|
| "daysAgo"
|
||||||
|
| "currentWeek"
|
||||||
|
| "currentMonth"
|
||||||
|
| "currentYear";
|
||||||
|
|
||||||
|
type DefaultDateFilterValue =
|
||||||
|
| (
|
||||||
|
| "today"
|
||||||
|
| "tomorrow"
|
||||||
|
| "yesterday"
|
||||||
|
| "oneWeekAgo"
|
||||||
|
| "oneWeekFromNow"
|
||||||
|
| "oneMonthAgo"
|
||||||
|
| "oneMonthFromNow"
|
||||||
|
)
|
||||||
|
| ["exactDay" | "exactTimestamp", string]
|
||||||
|
| ["daysAgo" | "daysFromNow", number];
|
||||||
|
|
||||||
|
type DateFilters = {
|
||||||
|
"=": DefaultDateFilterValue;
|
||||||
|
"!=": DefaultDateFilterValue;
|
||||||
|
"<": DefaultDateFilterValue;
|
||||||
|
">": DefaultDateFilterValue;
|
||||||
|
"<=": DefaultDateFilterValue;
|
||||||
|
">=": DefaultDateFilterValue;
|
||||||
|
isWithIn:
|
||||||
|
| IsWithinOperatorValue
|
||||||
|
| { value: "daysAgo" | "daysFromNow"; date: number };
|
||||||
|
isEmpty: boolean;
|
||||||
|
isNotEmpty: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DateAggregations = {
|
||||||
|
empty: number;
|
||||||
|
filled: number;
|
||||||
|
unique: number;
|
||||||
|
percentEmpty: number;
|
||||||
|
percentFilled: number;
|
||||||
|
percentUnique: number;
|
||||||
|
min: number | null;
|
||||||
|
max: number | null;
|
||||||
|
daysRange: number | null;
|
||||||
|
monthRange: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TextFilters = {
|
||||||
|
"=": string;
|
||||||
|
"!=": string;
|
||||||
|
caseEqual: string;
|
||||||
|
hasAnyOf: string[];
|
||||||
|
contains: string;
|
||||||
|
startsWith: string;
|
||||||
|
endsWith: string;
|
||||||
|
doesNotContain: string;
|
||||||
|
isEmpty: never;
|
||||||
|
isNotEmpty: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LinkFilters = {
|
||||||
|
hasAnyOf: number[];
|
||||||
|
hasAllOf: number[];
|
||||||
|
isExactly: number[];
|
||||||
|
"=": number;
|
||||||
|
hasNoneOf: number[];
|
||||||
|
contains: string;
|
||||||
|
doesNotContain: string;
|
||||||
|
isEmpty: never;
|
||||||
|
isNotEmpty: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SelectFilters<O extends readonly string[]> = {
|
||||||
|
hasAnyOf: O[number][];
|
||||||
|
hasAllOf: O[number][];
|
||||||
|
isExactly: O[number][];
|
||||||
|
"=": O[number];
|
||||||
|
hasNoneOf: O[number][];
|
||||||
|
contains: string;
|
||||||
|
doesNotContain: string;
|
||||||
|
isEmpty: never;
|
||||||
|
isNotEmpty: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LinkAggregations = {
|
||||||
|
empty: number;
|
||||||
|
filled: number;
|
||||||
|
percentEmpty: number;
|
||||||
|
percentFilled: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type NumberFilters = {
|
||||||
|
"=": number;
|
||||||
|
"!=": number;
|
||||||
|
">": number;
|
||||||
|
">=": number;
|
||||||
|
"<": number;
|
||||||
|
"<=": number;
|
||||||
|
hasAnyOf: number[];
|
||||||
|
hasNoneOf: number[];
|
||||||
|
isEmpty: never;
|
||||||
|
isNotEmpty: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
type NumberAggregations = {
|
||||||
|
sum: number;
|
||||||
|
average: number;
|
||||||
|
median: number;
|
||||||
|
min: number | null;
|
||||||
|
max: number | null;
|
||||||
|
range: number;
|
||||||
|
standardDeviation: number;
|
||||||
|
histogram: Record<string, number>;
|
||||||
|
empty: number;
|
||||||
|
filled: number;
|
||||||
|
unique: number;
|
||||||
|
percentEmpty: number;
|
||||||
|
percentFilled: number;
|
||||||
|
percentUnique: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CheckboxFilters = {
|
||||||
|
"=": number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Column types
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ColumnType<
|
||||||
|
S,
|
||||||
|
U,
|
||||||
|
I,
|
||||||
|
R extends boolean,
|
||||||
|
F extends { [key: string]: any } = object,
|
||||||
|
A extends { [key: string]: any } = object
|
||||||
|
> = {
|
||||||
|
raw: S;
|
||||||
|
insert: I;
|
||||||
|
update: U;
|
||||||
|
filters: F;
|
||||||
|
aggregations: A;
|
||||||
|
isRequired: R;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DateColumnType<R extends boolean> = ColumnType<
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
R,
|
||||||
|
DateFilters,
|
||||||
|
DateAggregations
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type TextColumnType<R extends boolean> = ColumnType<
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
R,
|
||||||
|
TextFilters
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type ALinkColumnType<
|
||||||
|
T extends string,
|
||||||
|
S,
|
||||||
|
U,
|
||||||
|
I,
|
||||||
|
R extends boolean,
|
||||||
|
F extends { [key: string]: any } = LinkFilters,
|
||||||
|
A extends LinkAggregations = LinkAggregations
|
||||||
|
> = ColumnType<S, U, I, R, F, A> & {
|
||||||
|
linkedTo: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LinkColumnType<
|
||||||
|
T extends string,
|
||||||
|
R extends boolean
|
||||||
|
> = ALinkColumnType<
|
||||||
|
T,
|
||||||
|
object,
|
||||||
|
number | number[] | { newIds: number[]; deletedIds: number[] },
|
||||||
|
number | number[],
|
||||||
|
R
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type AttachmentColumnType<R extends boolean> = ALinkColumnType<
|
||||||
|
"attachmentTable",
|
||||||
|
Attachment[],
|
||||||
|
Attachment[] | { newIds: number[]; deletedIds: number[] } | number[],
|
||||||
|
Attachment[] | number[],
|
||||||
|
R
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type NumberColumnType<R extends boolean> = ColumnType<
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
R,
|
||||||
|
NumberFilters,
|
||||||
|
NumberAggregations
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type CheckboxColumnType<R extends boolean> = ColumnType<
|
||||||
|
boolean,
|
||||||
|
boolean,
|
||||||
|
boolean,
|
||||||
|
R,
|
||||||
|
CheckboxFilters
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type AutoGeneratedNumberColumnType = ColumnType<
|
||||||
|
number,
|
||||||
|
never,
|
||||||
|
never,
|
||||||
|
false,
|
||||||
|
NumberFilters,
|
||||||
|
NumberAggregations
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type AutoGeneratedDateColumnType = ColumnType<
|
||||||
|
string,
|
||||||
|
never,
|
||||||
|
never,
|
||||||
|
false,
|
||||||
|
DateFilters,
|
||||||
|
DateAggregations
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type SingleSelectColumnType<
|
||||||
|
O extends readonly string[],
|
||||||
|
R extends boolean
|
||||||
|
> = ALinkColumnType<
|
||||||
|
"selectTable",
|
||||||
|
O[number],
|
||||||
|
O[number] | O[number][],
|
||||||
|
O[number] | O[number][],
|
||||||
|
R,
|
||||||
|
SelectFilters<O>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type TableRaws<T extends keyof TaylorDatabase> = {
|
||||||
|
[K in keyof TaylorDatabase[T]]: TaylorDatabase[T][K] extends ColumnType<
|
||||||
|
infer S,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
infer R,
|
||||||
|
any,
|
||||||
|
any
|
||||||
|
>
|
||||||
|
? R extends true
|
||||||
|
? S
|
||||||
|
: S | undefined
|
||||||
|
: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TableInserts<T extends keyof TaylorDatabase> = {
|
||||||
|
[K in keyof TaylorDatabase[T]]: TaylorDatabase[T][K] extends ColumnType<
|
||||||
|
any,
|
||||||
|
infer I,
|
||||||
|
any,
|
||||||
|
infer R,
|
||||||
|
any,
|
||||||
|
any
|
||||||
|
>
|
||||||
|
? R extends true
|
||||||
|
? I
|
||||||
|
: I | undefined
|
||||||
|
: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TableUpdates<T extends keyof TaylorDatabase> = {
|
||||||
|
[K in keyof TaylorDatabase[T]]: TaylorDatabase[T][K] extends ColumnType<
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
infer U,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
any
|
||||||
|
>
|
||||||
|
? U
|
||||||
|
: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SelectTable = {
|
||||||
|
id: AutoGeneratedNumberColumnType;
|
||||||
|
name: TextColumnType<true>;
|
||||||
|
color: TextColumnType<true>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AttachmentTable = {
|
||||||
|
id: AutoGeneratedNumberColumnType;
|
||||||
|
name: TextColumnType<true>;
|
||||||
|
metadata: TextColumnType<true>;
|
||||||
|
size: NumberColumnType<true>;
|
||||||
|
fileType: TextColumnType<true>;
|
||||||
|
url: TextColumnType<true>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CollaboratorsTable = {
|
||||||
|
id: AutoGeneratedNumberColumnType;
|
||||||
|
name: TextColumnType<true>;
|
||||||
|
emailAddress: TextColumnType<true>;
|
||||||
|
avatar: TextColumnType<true>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TaylorDatabase = {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Internal tables, these tables can not be queried directly.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
selectTable: SelectTable;
|
||||||
|
attachmentTable: AttachmentTable;
|
||||||
|
collaboratorsTable: CollaboratorsTable;
|
||||||
|
apps: AppsTable;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type VmStatusOptions = ["Active", "Inactive", "Pending", "Errored"];
|
||||||
|
export type AppStatusOptions = ["Active", "Inactive", "Pending", "Errored"];
|
||||||
|
|
||||||
|
export type AppsTable = {
|
||||||
|
name: TextColumnType<true>;
|
||||||
|
slug: TextColumnType<true>;
|
||||||
|
description: TextColumnType<false>;
|
||||||
|
templateRepoUrl: TextColumnType<true>;
|
||||||
|
devUrl: TextColumnType<false>;
|
||||||
|
opencodeUrl: TextColumnType<false>;
|
||||||
|
isPublic: CheckboxColumnType<false>;
|
||||||
|
isPublished: CheckboxColumnType<false>;
|
||||||
|
status: SingleSelectColumnType<VmStatusOptions, false>;
|
||||||
|
appStatus: SingleSelectColumnType<AppStatusOptions, false>;
|
||||||
|
icon: TextColumnType<false>;
|
||||||
|
color: TextColumnType<false>;
|
||||||
|
healthcheck: TextColumnType<false>;
|
||||||
|
publishUrl: TextColumnType<false>;
|
||||||
|
};
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>task tracker</title>
|
<title>blank</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user