@usecontextlayer/cli 0.14.3 → 0.15.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/README.md +36 -0
- package/dist/auth-CMrKVwsH.mjs +6847 -0
- package/dist/auth-CMrKVwsH.mjs.map +1 -0
- package/dist/auth.d.mts +23 -0
- package/dist/auth.d.mts.map +1 -0
- package/dist/auth.mjs +2 -0
- package/dist/cli.mjs +141 -38
- package/dist/cli.mjs.map +1 -1
- package/dist/{resources-DEdwfmku.mjs → resources-5FTDu19i.mjs} +152 -5619
- package/dist/resources-5FTDu19i.mjs.map +1 -0
- package/dist/rolldown-runtime-EAsCxrDo.mjs +14 -0
- package/dist/template/AGENTS.md +13 -0
- package/dist/template/README.md +44 -0
- package/dist/template/app/root.tsx +28 -0
- package/dist/template/app/routes/home.tsx +16 -3
- package/dist/template/databases/updates/drizzle.config.ts +8 -0
- package/dist/template/databases/updates/migrations/0000_create_updates.sql +6 -0
- package/dist/template/databases/updates/migrations/meta/0000_snapshot.json +14 -0
- package/dist/template/databases/updates/migrations/meta/_journal.json +13 -0
- package/dist/template/databases/updates/migrations/migrations.js +10 -0
- package/dist/template/databases/updates/schema.ts +8 -0
- package/dist/template/workers/app.ts +2 -1
- package/dist/template/workers/local-resources/index.ts +1 -0
- package/dist/template/workers/local-resources/wrangler.jsonc +19 -0
- package/dist/vite.d.mts +4426 -2
- package/dist/vite.d.mts.map +1 -1
- package/dist/vite.mjs +92 -8
- package/dist/vite.mjs.map +1 -1
- package/package.json +19 -3
- package/dist/resources-DEdwfmku.mjs.map +0 -1
- package/dist/template/workers/local-tools/index.ts +0 -1
- package/dist/template/workers/local-tools/wrangler.jsonc +0 -5
package/dist/auth.d.mts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region env.d.ts
|
|
2
|
+
declare const env: {
|
|
3
|
+
CTX_AUTH_CLIENT_ID: string;
|
|
4
|
+
CTX_AUTH_ISSUER: string;
|
|
5
|
+
CTX_PLATFORM_URL: string;
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region auth.d.ts
|
|
9
|
+
type AuthEnv = Pick<typeof env, "CTX_AUTH_ISSUER" | "CTX_AUTH_CLIENT_ID">;
|
|
10
|
+
export declare class LoginRequiredError extends Error {
|
|
11
|
+
constructor(reason: string, options?: ErrorOptions);
|
|
12
|
+
}
|
|
13
|
+
export declare function getAccessToken(env: AuthEnv): Promise<string>;
|
|
14
|
+
export declare function getCurrentUser(env: AuthEnv, headers: HeadersInit): Promise<{
|
|
15
|
+
id: string;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function listOrganizations(env: AuthEnv, headers: HeadersInit): Promise<{
|
|
18
|
+
id: string;
|
|
19
|
+
slug: string;
|
|
20
|
+
}[]>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export type { AuthEnv };
|
|
23
|
+
//# sourceMappingURL=auth.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.mts","names":[],"sources":["../env.ts","../auth.ts"],"mappings":";cAMa;;;;;;;KCCD,UAAU,YAAY;qBAYrB,2BAA2B;EACvC,YAAY,gBAAgB,UAAU;;wBAiDjB,eAAe,KAAK,UAAO;wBAqC3B,eAAe,KAAK,SAAS,SAAS,cAAW;;;wBAOjD,kBAAkB,KAAK,SAAS,SAAS,cAAW"}
|
package/dist/auth.mjs
ADDED
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { t as __commonJSMin } from "./rolldown-runtime-EAsCxrDo.mjs";
|
|
3
|
+
import { S as uuid, a as login, b as unknown, c as array, d as hostname, f as literal, g as record, h as object, i as listOrganizations, l as boolean, n as getAccessToken, p as looseObject, r as getCurrentUser, t as LoginRequiredError, v as string, x as url, y as union } from "./auth-CMrKVwsH.mjs";
|
|
4
|
+
import { c as resourcesForConsumer, f as execa, i as ownerSchema, l as secretNameSchema, n as saveLocalConnection, o as joinApp, r as appResourceConfigurationSchema, s as env, t as prepareLocalResourceConfiguration, u as databaseQuerySchema } from "./resources-5FTDu19i.mjs";
|
|
3
5
|
import { cp, readFile, writeFile } from "node:fs/promises";
|
|
4
6
|
import path, { join } from "node:path";
|
|
5
7
|
import { EventEmitter } from "node:events";
|
|
@@ -9,9 +11,6 @@ import process$1, { stdin, stdout } from "node:process";
|
|
|
9
11
|
import { stripVTControlCharacters, styleText } from "node:util";
|
|
10
12
|
import { fileURLToPath } from "node:url";
|
|
11
13
|
import l__default, { createInterface } from "node:readline";
|
|
12
|
-
//#region \0rolldown/runtime.js
|
|
13
|
-
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
14
|
-
//#endregion
|
|
15
14
|
//#region ../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/lib/error.js
|
|
16
15
|
/**
|
|
17
16
|
* CommanderError class
|
|
@@ -2968,13 +2967,40 @@ function useColor() {
|
|
|
2968
2967
|
new Command();
|
|
2969
2968
|
//#endregion
|
|
2970
2969
|
//#region agents.ts
|
|
2971
|
-
const platformInstructions = "You are a ContextLayer app agent. The user message is the task for this run. Work in the app directory and return the task's result.";
|
|
2970
|
+
const platformInstructions = "You are a ContextLayer app agent. The user message is the task for this run. Work in the app directory and return the task's result. Use database_query to read and save persistent data in declared databases. Files are scratch data; schema changes belong in explicitly authored SQL migrations.";
|
|
2972
2971
|
async function runLocalAgent(name, prompt) {
|
|
2973
2972
|
const instructions = await readFile(join("agents", name, "instructions.md"), "utf8");
|
|
2974
|
-
const {
|
|
2973
|
+
const { createServer } = await import("vite");
|
|
2974
|
+
const { getPreparedLocalBindings, openLocalDatabases } = await import("./vite.mjs");
|
|
2975
|
+
const server = await createServer({
|
|
2976
|
+
logLevel: "error",
|
|
2977
|
+
server: {
|
|
2978
|
+
host: "127.0.0.1",
|
|
2979
|
+
port: 0
|
|
2980
|
+
}
|
|
2981
|
+
});
|
|
2982
|
+
await server.listen();
|
|
2983
|
+
const bindings = getPreparedLocalBindings(server);
|
|
2984
|
+
const configuration = resourcesForConsumer(bindings.configuration, {
|
|
2985
|
+
name,
|
|
2986
|
+
type: "agent"
|
|
2987
|
+
});
|
|
2988
|
+
const storage = await openLocalDatabases(bindings.workerName, configuration.databases);
|
|
2989
|
+
const { query, createSdkMcpServer, tool } = await import("@anthropic-ai/claude-agent-sdk");
|
|
2975
2990
|
const run = query({
|
|
2976
2991
|
options: {
|
|
2992
|
+
allowedTools: ["mcp__databases__database_query"],
|
|
2977
2993
|
cwd: process.cwd(),
|
|
2994
|
+
mcpServers: { databases: createSdkMcpServer({
|
|
2995
|
+
name: "databases",
|
|
2996
|
+
tools: [tool("database_query", "Query or update a declared persistent SQLite database using parameterized SQL.", databaseQuerySchema.shape, async ({ slug, sql, params }) => {
|
|
2997
|
+
const result = await storage.databases.query(slug, sql, params);
|
|
2998
|
+
return { content: [{
|
|
2999
|
+
text: JSON.stringify(result),
|
|
3000
|
+
type: "text"
|
|
3001
|
+
}] };
|
|
3002
|
+
})]
|
|
3003
|
+
}) },
|
|
2978
3004
|
settingSources: [],
|
|
2979
3005
|
systemPrompt: {
|
|
2980
3006
|
append: `${platformInstructions}\n\n${instructions}`,
|
|
@@ -3001,6 +3027,8 @@ async function runLocalAgent(name, prompt) {
|
|
|
3001
3027
|
throw new Error("Claude ended without a result");
|
|
3002
3028
|
} finally {
|
|
3003
3029
|
run.close();
|
|
3030
|
+
await storage.dispose();
|
|
3031
|
+
await server.close();
|
|
3004
3032
|
}
|
|
3005
3033
|
}
|
|
3006
3034
|
//#endregion
|
|
@@ -3861,7 +3889,7 @@ const connectionSchema = object({
|
|
|
3861
3889
|
alias: string().nullable(),
|
|
3862
3890
|
display_name: string().nullable(),
|
|
3863
3891
|
id: string(),
|
|
3864
|
-
status:
|
|
3892
|
+
status: literal("ACTIVE"),
|
|
3865
3893
|
toolkit: string()
|
|
3866
3894
|
});
|
|
3867
3895
|
const connectionsSchema = object({ connections: array(connectionSchema) });
|
|
@@ -3870,16 +3898,10 @@ const connectionsSchema = object({ connections: array(connectionSchema) });
|
|
|
3870
3898
|
const ownerOptionsSchema = object({ org: string().min(1).optional() });
|
|
3871
3899
|
async function resolveOwner(org, headers) {
|
|
3872
3900
|
if (!org) return {
|
|
3873
|
-
id: (await getCurrentUser(headers)).id,
|
|
3901
|
+
id: (await getCurrentUser(env, headers)).id,
|
|
3874
3902
|
type: "user"
|
|
3875
3903
|
};
|
|
3876
|
-
const
|
|
3877
|
-
if (!response.ok) throw new Error(`Organization lookup failed: HTTP ${response.status}`);
|
|
3878
|
-
const { organizations } = object({ organizations: array(object({
|
|
3879
|
-
id: string().min(1),
|
|
3880
|
-
slug: string()
|
|
3881
|
-
})) }).parse(await response.json());
|
|
3882
|
-
const organization = organizations.find((organization) => organization.slug === org);
|
|
3904
|
+
const organization = (await listOrganizations(env, headers)).find((organization) => organization.slug === org);
|
|
3883
3905
|
if (!organization) throw new Error("Organization is not in your memberships");
|
|
3884
3906
|
return {
|
|
3885
3907
|
id: organization.id,
|
|
@@ -3910,10 +3932,10 @@ async function assignAppConnection(target, slug, connectionId) {
|
|
|
3910
3932
|
if (connectionId === void 0 && !process.stdin.isTTY) throw new Error("Supply a connection ID when assigning without a terminal");
|
|
3911
3933
|
const url = new URL("/api/connections", env.CTX_PLATFORM_URL);
|
|
3912
3934
|
url.searchParams.set("toolkit", requirement.toolkit);
|
|
3913
|
-
const response = await fetch(url, { headers: { authorization: `Bearer ${await getAccessToken()}` } });
|
|
3935
|
+
const response = await fetch(url, { headers: { authorization: `Bearer ${await getAccessToken(env)}` } });
|
|
3914
3936
|
if (!response.ok) throw new Error(`Personal connection list failed: HTTP ${response.status}`);
|
|
3915
3937
|
const { connections } = connectionsSchema.parse(await response.json());
|
|
3916
|
-
if (connections.length === 0) throw new Error(`No personal ${requirement.toolkit} accounts. Run
|
|
3938
|
+
if (connections.length === 0) throw new Error(`No personal ${requirement.toolkit} accounts. Run npx @usecontextlayer/cli connections add ${requirement.toolkit}, approve access in your browser, then rerun this assignment.`);
|
|
3917
3939
|
if (connectionId === void 0) {
|
|
3918
3940
|
const selected = await select({
|
|
3919
3941
|
message: `Your ${requirement.toolkit} account for ${slug}`,
|
|
@@ -3932,6 +3954,7 @@ async function assignAppConnection(target, slug, connectionId) {
|
|
|
3932
3954
|
if (!connections.some(({ id }) => id === connectionId)) throw new Error(`Account ${connectionId} is not one of your ${requirement.toolkit} accounts`);
|
|
3933
3955
|
await saveLocalConnection(slug, connectionId);
|
|
3934
3956
|
console.log(`${slug} → ${connectionId}`);
|
|
3957
|
+
console.log("Start or restart pnpm dev to use this assignment.");
|
|
3935
3958
|
return;
|
|
3936
3959
|
}
|
|
3937
3960
|
const response = await fetch(new URL(`/api/apps/${encodeURIComponent(target)}/connections/assign`, env.CTX_PLATFORM_URL), {
|
|
@@ -3940,7 +3963,7 @@ async function assignAppConnection(target, slug, connectionId) {
|
|
|
3940
3963
|
slug
|
|
3941
3964
|
}),
|
|
3942
3965
|
headers: {
|
|
3943
|
-
authorization: `Bearer ${await getAccessToken()}`,
|
|
3966
|
+
authorization: `Bearer ${await getAccessToken(env)}`,
|
|
3944
3967
|
"content-type": "application/json"
|
|
3945
3968
|
},
|
|
3946
3969
|
method: "POST"
|
|
@@ -3971,7 +3994,7 @@ async function listAppConnections(target) {
|
|
|
3971
3994
|
const response = await fetch(new URL(`/api/apps/${encodeURIComponent(target)}/connections/list`, env.CTX_PLATFORM_URL), {
|
|
3972
3995
|
body: JSON.stringify({}),
|
|
3973
3996
|
headers: {
|
|
3974
|
-
authorization: `Bearer ${await getAccessToken()}`,
|
|
3997
|
+
authorization: `Bearer ${await getAccessToken(env)}`,
|
|
3975
3998
|
"content-type": "application/json"
|
|
3976
3999
|
},
|
|
3977
4000
|
method: "POST"
|
|
@@ -3988,7 +4011,7 @@ async function listAppConnections(target) {
|
|
|
3988
4011
|
})));
|
|
3989
4012
|
}
|
|
3990
4013
|
async function addConnection(toolkit, org) {
|
|
3991
|
-
const headers = { authorization: `Bearer ${await getAccessToken()}` };
|
|
4014
|
+
const headers = { authorization: `Bearer ${await getAccessToken(env)}` };
|
|
3992
4015
|
const owner = await resolveOwner(org, headers);
|
|
3993
4016
|
const response = await fetch(new URL(`/api/connections/${ownerPath(owner)}`, env.CTX_PLATFORM_URL), {
|
|
3994
4017
|
body: JSON.stringify({ toolkit }),
|
|
@@ -4001,10 +4024,12 @@ async function addConnection(toolkit, org) {
|
|
|
4001
4024
|
if (!response.ok) throw new Error(`Connection authorization failed: HTTP ${response.status}`);
|
|
4002
4025
|
const { redirect_url } = object({ redirect_url: url() }).parse(await response.json());
|
|
4003
4026
|
console.log(`Open this link to authorize the Owner's account:\n${redirect_url}`);
|
|
4004
|
-
console.log(
|
|
4027
|
+
console.log("After approving access in your browser, assign the account to an app's declared connection slug.");
|
|
4028
|
+
if (!org) console.log("From your app directory, choose your account for a slug in public/slate.json:\n npx @usecontextlayer/cli connection local assign <slug>");
|
|
4029
|
+
console.log(`For a hosted app, get the account ID and assign it:\n npx @usecontextlayer/cli connections${org ? ` --org '${org.replaceAll("'", "'\\''")}'` : ""} list\n npx @usecontextlayer/cli connection <AppID-or-hostname> assign <slug> <connection_id>`);
|
|
4005
4030
|
}
|
|
4006
4031
|
async function listConnections(org) {
|
|
4007
|
-
const headers = { authorization: `Bearer ${await getAccessToken()}` };
|
|
4032
|
+
const headers = { authorization: `Bearer ${await getAccessToken(env)}` };
|
|
4008
4033
|
const owner = await resolveOwner(org, headers);
|
|
4009
4034
|
const response = await fetch(new URL(`/api/connections/${ownerPath(owner)}`, env.CTX_PLATFORM_URL), { headers });
|
|
4010
4035
|
if (!response.ok) throw new Error(`Connection list failed: HTTP ${response.status}`);
|
|
@@ -4053,11 +4078,11 @@ async function gitCredential(operation) {
|
|
|
4053
4078
|
attributes.append(line.slice(0, separator), line.slice(separator + 1));
|
|
4054
4079
|
}
|
|
4055
4080
|
literal("authtype").parse(attributes.getAll("capability[]").find((c) => c === "authtype"));
|
|
4056
|
-
process.stdout.write(`capability[]=authtype\nauthtype=Bearer\ncredential=${await getAccessToken()}\n\n`);
|
|
4081
|
+
process.stdout.write(`capability[]=authtype\nauthtype=Bearer\ncredential=${await getAccessToken(env)}\n\n`);
|
|
4057
4082
|
}
|
|
4058
4083
|
//#endregion
|
|
4059
4084
|
//#region package.json
|
|
4060
|
-
var version = "0.
|
|
4085
|
+
var version = "0.15.0";
|
|
4061
4086
|
//#endregion
|
|
4062
4087
|
//#region secrets.ts
|
|
4063
4088
|
const secretOptionsSchema = ownerOptionsSchema.extend({ stdin: boolean().default(false) });
|
|
@@ -4071,7 +4096,7 @@ async function resolveSecretUrl(name, org, headers) {
|
|
|
4071
4096
|
return new URL(`/api/secrets/${ownerPath(owner)}/${encodeURIComponent(name)}`, env.CTX_PLATFORM_URL);
|
|
4072
4097
|
}
|
|
4073
4098
|
async function setSecret(name, options) {
|
|
4074
|
-
const headers = { authorization: `Bearer ${await getAccessToken()}` };
|
|
4099
|
+
const headers = { authorization: `Bearer ${await getAccessToken(env)}` };
|
|
4075
4100
|
const url = await resolveSecretUrl(name, options.org, headers);
|
|
4076
4101
|
let value;
|
|
4077
4102
|
if (options.stdin) {
|
|
@@ -4103,7 +4128,7 @@ async function setSecret(name, options) {
|
|
|
4103
4128
|
console.log(JSON.stringify(confirmationSchema.parse(await response.json())));
|
|
4104
4129
|
}
|
|
4105
4130
|
async function dropSecret(name, org) {
|
|
4106
|
-
const headers = { authorization: `Bearer ${await getAccessToken()}` };
|
|
4131
|
+
const headers = { authorization: `Bearer ${await getAccessToken(env)}` };
|
|
4107
4132
|
const response = await fetch(await resolveSecretUrl(name, org, headers), {
|
|
4108
4133
|
headers,
|
|
4109
4134
|
method: "DELETE"
|
|
@@ -4114,9 +4139,31 @@ async function dropSecret(name, org) {
|
|
|
4114
4139
|
//#endregion
|
|
4115
4140
|
//#region cli.ts
|
|
4116
4141
|
const program = new Command().name("ctx").description("ContextLayer CLI").version(version);
|
|
4117
|
-
program.
|
|
4142
|
+
program.addHelpText("afterAll", "\nRun without a global install: npx @usecontextlayer/cli <command>");
|
|
4143
|
+
async function authenticate(command) {
|
|
4144
|
+
try {
|
|
4145
|
+
return await getCurrentUser(env, { authorization: `Bearer ${await getAccessToken(env)}` });
|
|
4146
|
+
} catch (error) {
|
|
4147
|
+
if (error instanceof LoginRequiredError) command.error(error.message);
|
|
4148
|
+
throw error;
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
async function requireLogin(command) {
|
|
4152
|
+
await authenticate(command);
|
|
4153
|
+
}
|
|
4154
|
+
program.command("init <directory>").description("Create a private ContextLayer app on main with React Router, Tailwind, and shadcn; requires login, Node.js 24+, pnpm, and Git author identity; sets the Git remote and prints how to start and publish the app").option("--org <slug>", "Create the app under an organization you belong to").hook("preAction", requireLogin).action(async (directory, options) => {
|
|
4118
4155
|
const input = object({ organization_slug: string().min(1).optional() }).parse({ organization_slug: options.org });
|
|
4119
|
-
const accessToken = await getAccessToken();
|
|
4156
|
+
const accessToken = await getAccessToken(env);
|
|
4157
|
+
const gitIdentity = await execa("git", ["var", "GIT_AUTHOR_IDENT"], {
|
|
4158
|
+
reject: false,
|
|
4159
|
+
stdio: [
|
|
4160
|
+
"inherit",
|
|
4161
|
+
"ignore",
|
|
4162
|
+
"inherit"
|
|
4163
|
+
]
|
|
4164
|
+
});
|
|
4165
|
+
process.exitCode = gitIdentity.exitCode;
|
|
4166
|
+
if (gitIdentity.exitCode !== 0) return;
|
|
4120
4167
|
const result = await execa("pnpm", [
|
|
4121
4168
|
"create",
|
|
4122
4169
|
"cloudflare@latest",
|
|
@@ -4128,6 +4175,11 @@ program.command("init <directory>").description("Create a Cloudflare React Route
|
|
|
4128
4175
|
"--",
|
|
4129
4176
|
"--yes"
|
|
4130
4177
|
], {
|
|
4178
|
+
env: {
|
|
4179
|
+
GIT_CONFIG_COUNT: "1",
|
|
4180
|
+
GIT_CONFIG_KEY_0: "init.defaultBranch",
|
|
4181
|
+
GIT_CONFIG_VALUE_0: "main"
|
|
4182
|
+
},
|
|
4131
4183
|
reject: false,
|
|
4132
4184
|
stdio: "inherit"
|
|
4133
4185
|
});
|
|
@@ -4135,7 +4187,18 @@ program.command("init <directory>").description("Create a Cloudflare React Route
|
|
|
4135
4187
|
if (result.exitCode !== 0) return;
|
|
4136
4188
|
const tsconfigPath = join(directory, "tsconfig.json");
|
|
4137
4189
|
const tsconfig = looseObject({ compilerOptions: record(string(), unknown()) }).parse(JSON.parse(await readFile(tsconfigPath, "utf8")));
|
|
4138
|
-
tsconfig.compilerOptions.paths = {
|
|
4190
|
+
tsconfig.compilerOptions.paths = {
|
|
4191
|
+
"@/*": ["./*"],
|
|
4192
|
+
"~/*": ["./app/*"]
|
|
4193
|
+
};
|
|
4194
|
+
const cloudflareTsconfigPath = join(directory, "tsconfig.cloudflare.json");
|
|
4195
|
+
const cloudflareTsconfig = looseObject({
|
|
4196
|
+
compilerOptions: record(string(), unknown()),
|
|
4197
|
+
include: array(string())
|
|
4198
|
+
}).parse(JSON.parse(await readFile(cloudflareTsconfigPath, "utf8")));
|
|
4199
|
+
cloudflareTsconfig.compilerOptions.paths = tsconfig.compilerOptions.paths;
|
|
4200
|
+
cloudflareTsconfig.include.push("databases/**/schema.ts");
|
|
4201
|
+
await writeFile(cloudflareTsconfigPath, `${JSON.stringify(cloudflareTsconfig, null, 2)}\n`);
|
|
4139
4202
|
await writeFile(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`);
|
|
4140
4203
|
const ui = await execa("pnpm", [
|
|
4141
4204
|
"dlx",
|
|
@@ -4172,7 +4235,8 @@ program.command("init <directory>").description("Create a Cloudflare React Route
|
|
|
4172
4235
|
const install = await execa("pnpm", [
|
|
4173
4236
|
"add",
|
|
4174
4237
|
`@usecontextlayer/sdk@${version}`,
|
|
4175
|
-
"zod@^4.4.3"
|
|
4238
|
+
"zod@^4.4.3",
|
|
4239
|
+
"drizzle-orm@0.45.3"
|
|
4176
4240
|
], {
|
|
4177
4241
|
cwd: directory,
|
|
4178
4242
|
reject: false,
|
|
@@ -4180,6 +4244,21 @@ program.command("init <directory>").description("Create a Cloudflare React Route
|
|
|
4180
4244
|
});
|
|
4181
4245
|
process.exitCode = install.exitCode;
|
|
4182
4246
|
if (install.exitCode !== 0) return;
|
|
4247
|
+
const migrationTool = await execa("pnpm", [
|
|
4248
|
+
"add",
|
|
4249
|
+
"-D",
|
|
4250
|
+
"drizzle-kit@0.31.11"
|
|
4251
|
+
], {
|
|
4252
|
+
cwd: directory,
|
|
4253
|
+
reject: false,
|
|
4254
|
+
stdio: "inherit"
|
|
4255
|
+
});
|
|
4256
|
+
process.exitCode = migrationTool.exitCode;
|
|
4257
|
+
if (migrationTool.exitCode !== 0) return;
|
|
4258
|
+
const packagePath = join(directory, "package.json");
|
|
4259
|
+
const projectPackage = looseObject({ scripts: record(string(), string()) }).parse(JSON.parse(await readFile(packagePath, "utf8")));
|
|
4260
|
+
projectPackage.scripts["db:migration"] = "drizzle-kit generate --custom";
|
|
4261
|
+
await writeFile(packagePath, `${JSON.stringify(projectPackage, null, 2)}\n`);
|
|
4183
4262
|
const response = await fetch(new URL("/api/apps", env.CTX_PLATFORM_URL), {
|
|
4184
4263
|
body: JSON.stringify(input),
|
|
4185
4264
|
headers: {
|
|
@@ -4205,33 +4284,57 @@ program.command("init <directory>").description("Create a Cloudflare React Route
|
|
|
4205
4284
|
stdio: "inherit"
|
|
4206
4285
|
});
|
|
4207
4286
|
process.exitCode = git.exitCode;
|
|
4287
|
+
if (git.exitCode !== 0) return;
|
|
4288
|
+
console.log(`
|
|
4289
|
+
ContextLayer app created: ${app.id}
|
|
4290
|
+
Dashboard: ${new URL(`/dashboard/apps/${app.id}`, env.CTX_PLATFORM_URL).href}
|
|
4291
|
+
Hosted URL: https://${app.public_hostname}/ (available after deployment)
|
|
4292
|
+
|
|
4293
|
+
Start locally:
|
|
4294
|
+
cd '${directory.replaceAll("'", "'\\''")}'
|
|
4295
|
+
pnpm dev
|
|
4296
|
+
Open the local URL printed by the development server.
|
|
4297
|
+
|
|
4298
|
+
Publish your changes:
|
|
4299
|
+
git add .
|
|
4300
|
+
git commit
|
|
4301
|
+
git push -u origin main
|
|
4302
|
+
|
|
4303
|
+
See README.md for the development and connection workflow.
|
|
4304
|
+
`);
|
|
4208
4305
|
});
|
|
4209
4306
|
program.command("login").description("Sign in by approving a browser code and configure Git credentials for ContextLayer repositories").action(async () => {
|
|
4210
|
-
await login();
|
|
4307
|
+
await login(env);
|
|
4211
4308
|
await setupGit(new URL(import.meta.url));
|
|
4212
4309
|
console.log("Git credentials configured for ContextLayer.");
|
|
4213
4310
|
});
|
|
4214
|
-
program.command("git-credential <operation>").description("Git credential helper using the saved login; invoked automatically by Git").
|
|
4215
|
-
|
|
4216
|
-
|
|
4311
|
+
program.command("git-credential <operation>").description("Git credential helper using the saved login; invoked automatically by Git").hook("preAction", async (command) => {
|
|
4312
|
+
if (command.args[0] === "get") await requireLogin(command);
|
|
4313
|
+
}).action(gitCredential);
|
|
4314
|
+
program.command("whoami").description("Show your ContextLayer user ID verified by the auth server").action(async (_options, command) => {
|
|
4315
|
+
console.log((await authenticate(command)).id);
|
|
4316
|
+
});
|
|
4317
|
+
program.command("join").argument("<target>", "AppID, public hostname, or local (run from the app root)").description("Join a private app you can view using ctx login, enabling member-scoped unattended work; returns app and user IDs").hook("preAction", requireLogin).action(async (target) => {
|
|
4217
4318
|
await joinApp(union([
|
|
4218
4319
|
literal("local"),
|
|
4219
4320
|
uuid(),
|
|
4220
4321
|
hostname()
|
|
4221
4322
|
]).parse(target));
|
|
4222
4323
|
});
|
|
4223
|
-
const connections = program.command("connections").description("Manage Owner-owned connected accounts or list app and agent connection requirements; local reads only the current folder and its saved development choices").option("--org <org_slug>", "Manage this organization's connections; defaults to the signed-in user").argument("[target]", "AppID or public hostname (pushed requirements), or local (current folder; no platform request)").addArgument(new Argument("[operation]", "App connection operation").choices(["list"])).
|
|
4324
|
+
const connections = program.command("connections").description("Manage Owner-owned connected accounts or list app and agent connection requirements; local reads only the current folder and its saved development choices").option("--org <org_slug>", "Manage this organization's connections; defaults to the signed-in user").argument("[target]", "AppID or public hostname (pushed requirements), or local (current folder; no platform request)").addArgument(new Argument("[operation]", "App connection operation").choices(["list"])).hook("preAction", async (command, actionCommand) => {
|
|
4325
|
+
if (actionCommand !== command || command.args[0] !== "local") await requireLogin(actionCommand);
|
|
4326
|
+
}).action(async (target, operation) => {
|
|
4224
4327
|
if (!target || !operation) connections.error("Expected: ctx connections <target> list");
|
|
4225
4328
|
await listAppConnections(target);
|
|
4226
4329
|
});
|
|
4227
4330
|
connections.command("list").description("List the selected Owner's Composio accounts with their toolkit, identity, and status").action(async (_options, command) => {
|
|
4228
4331
|
await listConnections(ownerOptionsSchema.parse(command.optsWithGlobals()).org);
|
|
4229
4332
|
});
|
|
4230
|
-
connections.command("add <toolkit>").description("
|
|
4333
|
+
connections.command("add <toolkit>").description("Authorize the selected Owner's account in your browser (for example, gmail), then assign it to an app's declared connection slug with ctx connection").action(async (toolkit, _options, command) => {
|
|
4231
4334
|
const options = ownerOptionsSchema.parse(command.optsWithGlobals());
|
|
4232
4335
|
await addConnection(string().min(1).parse(toolkit), options.org);
|
|
4233
4336
|
});
|
|
4234
|
-
program.command("connection").description("Bind a declared slug: local saves your personal account in .contextlayer/connections.json; AppID/hostname saves a hosted assignment under its declared Owner").argument("<target>", "AppID, public hostname, or local (run from the app root)").addArgument(new Argument("<operation>").choices(["assign"])).argument("<slug>", "Connection
|
|
4337
|
+
program.command("connection").description("Bind a declared slug: local saves your personal account in .contextlayer/connections.json; AppID/hostname saves a hosted assignment under its declared Owner").argument("<target>", "AppID, public hostname, or local (run from the app root)").addArgument(new Argument("<operation>").choices(["assign"])).argument("<slug>", "Connection slug declared in your Slate (for example, work-email)").argument("[connection_id]", "Connected account ID; omit locally to choose your accounts for the declared toolkit; required for hosted targets").hook("preAction", requireLogin).action(async (target, _operation, slug, connectionId) => {
|
|
4235
4338
|
const input = connectionAssignmentSchema.parse({
|
|
4236
4339
|
connection_id: connectionId,
|
|
4237
4340
|
slug,
|
|
@@ -4249,7 +4352,7 @@ program.command("agent").addArgument(new Argument("<target>", "Run from the app
|
|
|
4249
4352
|
});
|
|
4250
4353
|
console.log(await runLocalAgent(input.name, input.prompt));
|
|
4251
4354
|
});
|
|
4252
|
-
const secrets = program.command("secrets").description("Save or drop Owner-owned shared secrets through the authenticated platform").option("--org <org_slug>", "Use this organization's Owner; defaults to the signed-in user");
|
|
4355
|
+
const secrets = program.command("secrets").description("Save or drop Owner-owned shared secrets through the authenticated platform").option("--org <org_slug>", "Use this organization's Owner; defaults to the signed-in user").hook("preAction", requireLogin);
|
|
4253
4356
|
secrets.command("set").argument("<name>", "Secret name: uppercase letters, digits, and underscores; cannot start with a digit").description("Save a secret with a masked password prompt; returns only Owner and name").option("--stdin", "Read the exact UTF-8 value from stdin instead of prompting; preserves whitespace and newlines").action(async (name, _options, command) => {
|
|
4254
4357
|
await setSecret(secretNameSchema.parse(name), secretOptionsSchema.parse(command.optsWithGlobals()));
|
|
4255
4358
|
});
|