@usecontextlayer/cli 0.16.1 → 0.17.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 (53) hide show
  1. package/README.md +4 -4
  2. package/dist/auth-BL9AxZGb.mjs +1214 -0
  3. package/dist/auth-BL9AxZGb.mjs.map +1 -0
  4. package/dist/auth.d.mts +17 -2
  5. package/dist/auth.d.mts.map +1 -1
  6. package/dist/auth.mjs +1 -2
  7. package/dist/cli.mjs +153 -195
  8. package/dist/cli.mjs.map +1 -1
  9. package/dist/database-codegen-C5TEhR5_.mjs +24 -0
  10. package/dist/database-codegen-C5TEhR5_.mjs.map +1 -0
  11. package/dist/database-dialect-BLWWgYGo.mjs +56 -0
  12. package/dist/database-dialect-BLWWgYGo.mjs.map +1 -0
  13. package/dist/database.d.mts +35 -0
  14. package/dist/database.d.mts.map +1 -0
  15. package/dist/database.mjs +32 -0
  16. package/dist/database.mjs.map +1 -0
  17. package/dist/databases-SvX3IgqC.mjs +23 -0
  18. package/dist/databases-SvX3IgqC.mjs.map +1 -0
  19. package/dist/env-C_tllJ4u.mjs +98 -0
  20. package/dist/env-C_tllJ4u.mjs.map +1 -0
  21. package/dist/local-databases-ChvVgJH6.mjs +1 -0
  22. package/dist/local-databases-DVI3ngFq.mjs +53 -0
  23. package/dist/local-databases-DVI3ngFq.mjs.map +1 -0
  24. package/dist/{resources-B-EYHG1j.mjs → resources-BhJbHzU0.mjs} +24 -175
  25. package/dist/resources-BhJbHzU0.mjs.map +1 -0
  26. package/dist/{auth-DOsE8GwJ.mjs → schemas-D9UReYiT.mjs} +37 -1212
  27. package/dist/schemas-D9UReYiT.mjs.map +1 -0
  28. package/dist/schemas-DeaNWrS-.d.mts +2213 -0
  29. package/dist/schemas-DeaNWrS-.d.mts.map +1 -0
  30. package/dist/template.tar.gz +0 -0
  31. package/dist/vite.d.mts +5 -4330
  32. package/dist/vite.d.mts.map +1 -1
  33. package/dist/vite.mjs +16 -67
  34. package/dist/vite.mjs.map +1 -1
  35. package/package.json +12 -7
  36. package/dist/auth-DOsE8GwJ.mjs.map +0 -1
  37. package/dist/resources-B-EYHG1j.mjs.map +0 -1
  38. package/dist/rolldown-runtime-EAsCxrDo.mjs +0 -14
  39. package/dist/template/AGENTS.md +0 -13
  40. package/dist/template/README.md +0 -46
  41. package/dist/template/app/root.tsx +0 -66
  42. package/dist/template/app/routes/home.tsx +0 -25
  43. package/dist/template/databases/updates/drizzle.config.ts +0 -8
  44. package/dist/template/databases/updates/migrations/0000_create_updates.sql +0 -6
  45. package/dist/template/databases/updates/migrations/meta/0000_snapshot.json +0 -14
  46. package/dist/template/databases/updates/migrations/meta/_journal.json +0 -13
  47. package/dist/template/databases/updates/migrations/migrations.js +0 -10
  48. package/dist/template/databases/updates/schema.ts +0 -8
  49. package/dist/template/public/slate.json +0 -4
  50. package/dist/template/vite.config.ts +0 -9
  51. package/dist/template/workers/app.ts +0 -8
  52. package/dist/template/workers/local-resources/index.ts +0 -6
  53. package/dist/template/workers/local-resources/wrangler.jsonc +0 -19
package/dist/cli.mjs CHANGED
@@ -1,16 +1,21 @@
1
1
  #!/usr/bin/env node
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-DOsE8GwJ.mjs";
4
- import { a as env, c as resourcesForConsumer, f as execa, i as joinSlate, l as secretNameSchema, n as saveLocalConnection, o as slateResourceConfigurationSchema, s as ownerSchema, t as prepareLocalResourceConfiguration, u as databaseQuerySchema } from "./resources-B-EYHG1j.mjs";
5
- import { cp, readFile, writeFile } from "node:fs/promises";
6
- import path, { join } from "node:path";
2
+ import { a as login, i as listOrganizations, n as getAccessToken, r as getCurrentUser, t as LoginRequiredError } from "./auth-BL9AxZGb.mjs";
3
+ import { a as boolean, c as literal, g as uuid, h as url, i as array, o as discriminatedUnion, p as string, r as _undefined, s as hostname, u as object } from "./schemas-D9UReYiT.mjs";
4
+ import { a as databaseSlugSchema, l as secretNameSchema, n as env, s as resourcesForConsumer } from "./env-C_tllJ4u.mjs";
5
+ import { a as noCommitSchema, c as execa, n as prepareLocalResourceConfiguration, o as slateResourceConfigurationSchema, r as saveLocalConnection, s as ownerSchema } from "./resources-BhJbHzU0.mjs";
6
+ import { t as databaseQuerySchema } from "./databases-SvX3IgqC.mjs";
7
+ import { readFile } from "node:fs/promises";
8
+ import path, { join, resolve } from "node:path";
9
+ import { fileURLToPath } from "node:url";
7
10
  import { EventEmitter } from "node:events";
8
11
  import childProcess from "node:child_process";
9
12
  import fs from "node:fs";
10
13
  import process$1, { stdin, stdout } from "node:process";
11
14
  import { stripVTControlCharacters, styleText } from "node:util";
12
- import { fileURLToPath } from "node:url";
13
15
  import l__default, { createInterface } from "node:readline";
16
+ //#region \0rolldown/runtime.js
17
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
18
+ //#endregion
14
19
  //#region ../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/lib/error.js
15
20
  /**
16
21
  * CommanderError class
@@ -2966,12 +2971,72 @@ function useColor() {
2966
2971
  }
2967
2972
  new Command();
2968
2973
  //#endregion
2974
+ //#region ../shared/dist/connections.mjs
2975
+ const connectionSchema = object({
2976
+ alias: string().nullable(),
2977
+ display_name: string().nullable(),
2978
+ id: string(),
2979
+ status: literal("ACTIVE"),
2980
+ toolkit: string()
2981
+ });
2982
+ const connectionsSchema = object({ connections: array(connectionSchema) });
2983
+ //#endregion
2984
+ //#region owner.ts
2985
+ const ownerOptionsSchema = object({ org: string().min(1).optional() });
2986
+ async function resolveOwner(org, headers) {
2987
+ if (!org) return {
2988
+ id: (await getCurrentUser(env, headers)).id,
2989
+ type: "personal"
2990
+ };
2991
+ const organization = (await listOrganizations(env, headers)).find((organization) => organization.slug === org);
2992
+ if (!organization) throw new Error("Organization is not in your memberships");
2993
+ return {
2994
+ id: organization.id,
2995
+ type: "organization"
2996
+ };
2997
+ }
2998
+ function ownerPath(owner) {
2999
+ return `${owner.type === "organization" ? "org" : "personal"}/${encodeURIComponent(owner.id)}`;
3000
+ }
3001
+ //#endregion
3002
+ //#region accounts.ts
3003
+ async function connectAccount(toolkit, org) {
3004
+ const headers = { authorization: `Bearer ${await getAccessToken(env)}` };
3005
+ const owner = await resolveOwner(org, headers);
3006
+ const response = await fetch(new URL(`/api/connections/${ownerPath(owner)}`, env.CTX_PLATFORM_URL), {
3007
+ body: JSON.stringify({ toolkit }),
3008
+ headers: {
3009
+ ...headers,
3010
+ "content-type": "application/json"
3011
+ },
3012
+ method: "POST"
3013
+ });
3014
+ if (!response.ok) throw new Error(`Connecting account failed: HTTP ${response.status}`);
3015
+ const { redirect_url } = object({ redirect_url: url() }).parse(await response.json());
3016
+ console.log(`Open this link to connect the Owner's account:\n${redirect_url}`);
3017
+ console.log("After connecting in your browser, assign the connected account to a slate's declared connection.");
3018
+ if (!org) console.log("From your checkout, assign your connected account to a connection declared in slate.json:\n npx @usecontextlayer/cli connections local assign <slug>");
3019
+ console.log(`For a hosted slate, get the connected account ID and assign it:\n npx @usecontextlayer/cli accounts${org ? ` --org '${org.replaceAll("'", "'\\''")}'` : ""} list\n npx @usecontextlayer/cli connections <SlateID-or-hostname> assign <slug> <connection_id>`);
3020
+ }
3021
+ async function listAccounts(org) {
3022
+ const headers = { authorization: `Bearer ${await getAccessToken(env)}` };
3023
+ const owner = await resolveOwner(org, headers);
3024
+ const response = await fetch(new URL(`/api/connections/${ownerPath(owner)}`, env.CTX_PLATFORM_URL), { headers });
3025
+ if (!response.ok) throw new Error(`Connected account list failed: HTTP ${response.status}`);
3026
+ const { connections } = connectionsSchema.parse(await response.json());
3027
+ if (connections.length === 0) {
3028
+ console.log("No connected accounts.");
3029
+ return;
3030
+ }
3031
+ console.table(connections);
3032
+ }
3033
+ //#endregion
2969
3034
  //#region agents.ts
2970
- const platformInstructions = "You are an agent of a ContextLayer slate.The user message is the task for this run. Work in the checkout 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.";
3035
+ const platformInstructions = "You are an agent of a ContextLayer slate. The user message is the task for this run. Work in the checkout 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 Kysely migrations.";
2971
3036
  async function runLocalAgent(name, prompt) {
2972
3037
  const instructions = await readFile(join("agents", name, "instructions.md"), "utf8");
2973
3038
  const { createServer } = await import("vite");
2974
- const { getPreparedLocalBindings, openLocalDatabases } = await import("./vite.mjs");
3039
+ const { getPreparedLocalBindings } = await import("./vite.mjs");
2975
3040
  const server = await createServer({
2976
3041
  logLevel: "error",
2977
3042
  server: {
@@ -2985,6 +3050,7 @@ async function runLocalAgent(name, prompt) {
2985
3050
  name,
2986
3051
  type: "agent"
2987
3052
  });
3053
+ const { openLocalDatabases } = await import("./local-databases-ChvVgJH6.mjs");
2988
3054
  const storage = await openLocalDatabases(bindings.workerName, configuration.databases);
2989
3055
  const { query, createSdkMcpServer, tool } = await import("@anthropic-ai/claude-agent-sdk");
2990
3056
  const run = query({
@@ -3880,41 +3946,19 @@ ${f}
3880
3946
  };
3881
3947
  `${styleText("gray", S_BAR)}`;
3882
3948
  //#endregion
3883
- //#region ../shared/dist/connections.mjs
3884
- const connectionSchema = object({
3885
- alias: string().nullable(),
3886
- display_name: string().nullable(),
3887
- id: string(),
3888
- status: literal("ACTIVE"),
3889
- toolkit: string()
3890
- });
3891
- const connectionsSchema = object({ connections: array(connectionSchema) });
3892
- //#endregion
3893
3949
  //#region ../shared/dist/slate-connections.mjs
3894
3950
  const slateConnectionSchema = slateResourceConfigurationSchema.shape.connections.valueType.extend({ slug: string() });
3895
3951
  const slateConnectionsSchema = object({ connections: array(slateConnectionSchema) });
3896
3952
  //#endregion
3897
- //#region owner.ts
3898
- const ownerOptionsSchema = object({ org: string().min(1).optional() });
3899
- async function resolveOwner(org, headers) {
3900
- if (!org) return {
3901
- id: (await getCurrentUser(env, headers)).id,
3902
- type: "personal"
3903
- };
3904
- const organization = (await listOrganizations(env, headers)).find((organization) => organization.slug === org);
3905
- if (!organization) throw new Error("Organization is not in your memberships");
3906
- return {
3907
- id: organization.id,
3908
- type: "organization"
3909
- };
3910
- }
3911
- function ownerPath(owner) {
3912
- return `${owner.type === "organization" ? "org" : "personal"}/${encodeURIComponent(owner.id)}`;
3913
- }
3914
- //#endregion
3915
3953
  //#region connections.ts
3916
- const connectionAssignmentSchema = object({
3954
+ const connectionsCommandSchema = discriminatedUnion("operation", [object({
3955
+ connection_id: _undefined(),
3956
+ operation: literal("list"),
3957
+ slug: _undefined(),
3958
+ target: string().min(1)
3959
+ }), object({
3917
3960
  connection_id: string().min(1).optional(),
3961
+ operation: literal("assign"),
3918
3962
  slug: string().min(1),
3919
3963
  target: string().min(1)
3920
3964
  }).superRefine((input, ctx) => {
@@ -3923,7 +3967,7 @@ const connectionAssignmentSchema = object({
3923
3967
  message: "Hosted assignment requires a connection ID",
3924
3968
  path: ["connection_id"]
3925
3969
  });
3926
- });
3970
+ })]);
3927
3971
  async function assignSlateConnection(target, slug, connectionId) {
3928
3972
  if (target === "local") {
3929
3973
  const configuration = await prepareLocalResourceConfiguration();
@@ -3935,7 +3979,7 @@ async function assignSlateConnection(target, slug, connectionId) {
3935
3979
  const response = await fetch(url, { headers: { authorization: `Bearer ${await getAccessToken(env)}` } });
3936
3980
  if (!response.ok) throw new Error(`Personal connected account list failed: HTTP ${response.status}`);
3937
3981
  const { connections } = connectionsSchema.parse(await response.json());
3938
- if (connections.length === 0) throw new Error(`No personal connected accounts for ${requirement.toolkit}. Run npx @usecontextlayer/cli connections add ${requirement.toolkit} to connect an account in your browser, then rerun this assignment.`);
3982
+ if (connections.length === 0) throw new Error(`No personal connected accounts for ${requirement.toolkit}. Run npx @usecontextlayer/cli accounts add ${requirement.toolkit} to connect an account in your browser, then rerun this assignment.`);
3939
3983
  if (connectionId === void 0) {
3940
3984
  const accountId = await select({
3941
3985
  message: `Assign a connected ${requirement.toolkit} account to ${slug}`,
@@ -3968,6 +4012,7 @@ async function assignSlateConnection(target, slug, connectionId) {
3968
4012
  },
3969
4013
  method: "POST"
3970
4014
  });
4015
+ if (response.status === 409 && noCommitSchema.safeParse(await response.json()).success) throw new Error("Nothing has been pushed to this slate yet. Push to main before configuring hosted resources.");
3971
4016
  if (!response.ok) throw new Error(`Connection assignment failed: HTTP ${response.status}`);
3972
4017
  const assignment = object({
3973
4018
  connection_id: string(),
@@ -3975,21 +4020,13 @@ async function assignSlateConnection(target, slug, connectionId) {
3975
4020
  }).parse(await response.json());
3976
4021
  console.log(`${assignment.slug} → ${assignment.connection_id}`);
3977
4022
  }
3978
- async function listSlateConnections(target) {
4023
+ async function prepareSlateConnections(target) {
3979
4024
  if (target === "local") {
3980
4025
  const { connections } = await prepareLocalResourceConfiguration();
3981
- if (Object.keys(connections).length === 0) {
3982
- console.log("No connection requirements.");
3983
- return;
3984
- }
3985
- console.table(Object.entries(connections).map(([slug, { connection_id, consumers, mode, toolkit }]) => ({
3986
- connected_account: connection_id ?? "unassigned",
3987
- consumers: consumers.map((consumer) => consumer.type === "app" ? "app" : `agent:${consumer.name}`).join(", "),
3988
- mode,
3989
- slug,
3990
- toolkit
3991
- })));
3992
- return;
4026
+ return Object.entries(connections).map(([slug, connection]) => ({
4027
+ ...connection,
4028
+ slug
4029
+ }));
3993
4030
  }
3994
4031
  const response = await fetch(new URL(`/api/slates/${encodeURIComponent(target)}/connections/list`, env.CTX_PLATFORM_URL), {
3995
4032
  body: JSON.stringify({}),
@@ -3999,47 +4036,22 @@ async function listSlateConnections(target) {
3999
4036
  },
4000
4037
  method: "POST"
4001
4038
  });
4039
+ if (response.status === 409 && noCommitSchema.safeParse(await response.json()).success) throw new Error("Nothing has been pushed to this slate yet. Push to main before configuring hosted resources.");
4002
4040
  if (!response.ok) throw new Error(`Slate connections request failed: HTTP ${response.status}`);
4003
- const { connections } = slateConnectionsSchema.parse(await response.json());
4041
+ return slateConnectionsSchema.parse(await response.json()).connections;
4042
+ }
4043
+ async function listSlateConnections(target) {
4044
+ const connections = await prepareSlateConnections(target);
4004
4045
  if (!connections.length) {
4005
4046
  console.log("No connection requirements.");
4006
4047
  return;
4007
4048
  }
4008
- console.table(connections.map(({ connection_id, ...requirement }) => ({
4049
+ console.table(connections.map(({ connection_id, consumers, ...requirement }) => ({
4009
4050
  ...requirement,
4010
- connected_account: connection_id ?? "unassigned"
4051
+ connected_account: connection_id ?? "unassigned",
4052
+ consumers: consumers.map((consumer) => consumer.type === "app" ? "app" : `agent:${consumer.name}`).join(", ")
4011
4053
  })));
4012
4054
  }
4013
- async function connectAccount(toolkit, org) {
4014
- const headers = { authorization: `Bearer ${await getAccessToken(env)}` };
4015
- const owner = await resolveOwner(org, headers);
4016
- const response = await fetch(new URL(`/api/connections/${ownerPath(owner)}`, env.CTX_PLATFORM_URL), {
4017
- body: JSON.stringify({ toolkit }),
4018
- headers: {
4019
- ...headers,
4020
- "content-type": "application/json"
4021
- },
4022
- method: "POST"
4023
- });
4024
- if (!response.ok) throw new Error(`Connecting account failed: HTTP ${response.status}`);
4025
- const { redirect_url } = object({ redirect_url: url() }).parse(await response.json());
4026
- console.log(`Open this link to connect the Owner's account:\n${redirect_url}`);
4027
- console.log("After connecting in your browser, assign the connected account to a slate's declared connection.");
4028
- if (!org) console.log("From your checkout, assign your connected account to a connection declared in slate.json:\n npx @usecontextlayer/cli connection local assign <slug>");
4029
- console.log(`For a hosted slate, get the connected account ID and assign it:\n npx @usecontextlayer/cli connections${org ? ` --org '${org.replaceAll("'", "'\\''")}'` : ""} list\n npx @usecontextlayer/cli connection <SlateID-or-hostname> assign <slug> <connection_id>`);
4030
- }
4031
- async function listConnections(org) {
4032
- const headers = { authorization: `Bearer ${await getAccessToken(env)}` };
4033
- const owner = await resolveOwner(org, headers);
4034
- const response = await fetch(new URL(`/api/connections/${ownerPath(owner)}`, env.CTX_PLATFORM_URL), { headers });
4035
- if (!response.ok) throw new Error(`Connected account list failed: HTTP ${response.status}`);
4036
- const { connections } = connectionsSchema.parse(await response.json());
4037
- if (connections.length === 0) {
4038
- console.log("No connected accounts.");
4039
- return;
4040
- }
4041
- console.table(connections);
4042
- }
4043
4055
  //#endregion
4044
4056
  //#region git-credential.ts
4045
4057
  async function configureGit(cli) {
@@ -4082,7 +4094,7 @@ async function gitCredential(operation) {
4082
4094
  }
4083
4095
  //#endregion
4084
4096
  //#region package.json
4085
- var version = "0.16.1";
4097
+ var version = "0.17.0";
4086
4098
  //#endregion
4087
4099
  //#region secrets.ts
4088
4100
  const secretOptionsSchema = ownerOptionsSchema.extend({ stdin: boolean().default(false) });
@@ -4139,19 +4151,29 @@ async function dropSecret(name, org) {
4139
4151
  //#endregion
4140
4152
  //#region cli.ts
4141
4153
  const program = new Command().name("ctx").description("ContextLayer CLI").version(version);
4142
- program.addHelpText("afterAll", "\nRun without a global install: npx @usecontextlayer/cli <command>");
4154
+ program.addHelpText("afterAll", `\nEnvironment (current values):\n CTX_PLATFORM_URL ${env.CTX_PLATFORM_URL}\n CTX_AUTH_ISSUER ${env.CTX_AUTH_ISSUER}\n CTX_AUTH_CLIENT_ID ${env.CTX_AUTH_CLIENT_ID}\n CTX_CONFIG_DIR ${env.CTX_CONFIG_DIR} Saved-login directory; one login per directory.\n\nRun without a global install: npx @usecontextlayer/cli <command>`);
4143
4155
  async function authenticate(command) {
4144
4156
  try {
4145
4157
  return await getCurrentUser(env, { authorization: `Bearer ${await getAccessToken(env)}` });
4146
4158
  } catch (error) {
4147
- if (error instanceof LoginRequiredError) command.error(error.message);
4159
+ if (error instanceof LoginRequiredError) {
4160
+ const lines = [error.message];
4161
+ if (error.mismatch) {
4162
+ const { saved, expected } = error.mismatch;
4163
+ lines.push(`Saved issuer: ${saved.issuer}`, `Expected issuer: ${expected.issuer}`);
4164
+ if (saved.clientId !== expected.clientId) lines.push(`Saved client ID: ${saved.clientId}`, `Expected client ID: ${expected.clientId}`);
4165
+ }
4166
+ const settings = Object.entries(env).map(([key, value]) => `${key}='${value.replaceAll("'", "'\\''")}'`).join(" ");
4167
+ lines.push(`Run: ${settings} npx @usecontextlayer/cli login`);
4168
+ command.error(lines.join("\n"));
4169
+ }
4148
4170
  throw error;
4149
4171
  }
4150
4172
  }
4151
4173
  async function requireLogin(command) {
4152
4174
  await authenticate(command);
4153
4175
  }
4154
- program.command("init <directory>").description("Create a private ContextLayer slate 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 deploy the slate").option("--org <slug>", "Create the slate under an organization you belong to").hook("preAction", requireLogin).action(async (directory, options) => {
4176
+ program.command("init <directory>").description("Create a private ContextLayer slate on main with React Router, Tailwind, and shadcn; requires login, Node.js 22.22.0+, pnpm, and Git author identity; sets the Git remote and prints how to start and deploy the slate").option("--org <slug>", "Create the slate under an organization you belong to").hook("preAction", requireLogin).action(async (directory, options) => {
4155
4177
  const input = object({ organization_slug: string().min(1).optional() }).parse({ organization_slug: options.org });
4156
4178
  const accessToken = await getAccessToken(env);
4157
4179
  const gitIdentity = await execa("git", ["var", "GIT_AUTHOR_IDENT"], {
@@ -4164,15 +4186,15 @@ program.command("init <directory>").description("Create a private ContextLayer s
4164
4186
  });
4165
4187
  process.exitCode = gitIdentity.exitCode;
4166
4188
  if (gitIdentity.exitCode !== 0) return;
4167
- const result = await execa("pnpm", [
4168
- "create",
4169
- "cloudflare@latest",
4189
+ const result = await execa(process.execPath, [
4190
+ fileURLToPath(new URL("./dist/cli.js", import.meta.resolve("create-react-router/package.json"))),
4170
4191
  directory,
4171
- "--framework=react-router",
4172
- "--accept-defaults",
4173
- "--git",
4174
- "--no-deploy",
4175
- "--",
4192
+ "--template",
4193
+ fileURLToPath(new URL("./template.tar.gz", import.meta.url)),
4194
+ "--package-manager",
4195
+ "pnpm",
4196
+ "--install",
4197
+ "--git-init",
4176
4198
  "--yes"
4177
4199
  ], {
4178
4200
  env: {
@@ -4185,80 +4207,6 @@ program.command("init <directory>").description("Create a private ContextLayer s
4185
4207
  });
4186
4208
  process.exitCode = result.exitCode;
4187
4209
  if (result.exitCode !== 0) return;
4188
- const tsconfigPath = join(directory, "tsconfig.json");
4189
- const tsconfig = looseObject({ compilerOptions: record(string(), unknown()) }).parse(JSON.parse(await readFile(tsconfigPath, "utf8")));
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`);
4202
- await writeFile(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`);
4203
- const ui = await execa("pnpm", [
4204
- "dlx",
4205
- "shadcn@latest",
4206
- "init",
4207
- "--preset",
4208
- "nova",
4209
- "--base",
4210
- "base",
4211
- "--yes",
4212
- "--no-monorepo"
4213
- ], {
4214
- cwd: directory,
4215
- reject: false,
4216
- stdio: "inherit"
4217
- });
4218
- process.exitCode = ui.exitCode;
4219
- if (ui.exitCode !== 0) return;
4220
- await cp(new URL("./template/", import.meta.url), directory, { recursive: true });
4221
- const configure = await execa("pnpm", [
4222
- "config",
4223
- "set",
4224
- "--location=project",
4225
- "--json",
4226
- "minimumReleaseAgeExclude",
4227
- "[\"@usecontextlayer/*\"]"
4228
- ], {
4229
- cwd: directory,
4230
- reject: false,
4231
- stdio: "inherit"
4232
- });
4233
- process.exitCode = configure.exitCode;
4234
- if (configure.exitCode !== 0) return;
4235
- const install = await execa("pnpm", [
4236
- "add",
4237
- `@usecontextlayer/sdk@${version}`,
4238
- "zod@^4.4.3",
4239
- "drizzle-orm@0.45.3"
4240
- ], {
4241
- cwd: directory,
4242
- reject: false,
4243
- stdio: "inherit"
4244
- });
4245
- process.exitCode = install.exitCode;
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 checkoutPackage = looseObject({ scripts: record(string(), string()) }).parse(JSON.parse(await readFile(packagePath, "utf8")));
4260
- checkoutPackage.scripts["db:migration"] = "drizzle-kit generate --custom";
4261
- await writeFile(packagePath, `${JSON.stringify(checkoutPackage, null, 2)}\n`);
4262
4210
  const response = await fetch(new URL("/api/slates", env.CTX_PLATFORM_URL), {
4263
4211
  body: JSON.stringify(input),
4264
4212
  headers: {
@@ -4295,11 +4243,11 @@ Start locally:
4295
4243
  pnpm dev
4296
4244
  Open the local URL printed by the development server.
4297
4245
 
4298
- Deploy your changes:
4299
- git add .
4300
- git commit
4246
+ Deploy the committed starter:
4301
4247
  git push -u origin main
4302
4248
 
4249
+ Commit your own changes before subsequent pushes.
4250
+
4303
4251
  See README.md for the development and connection workflow.
4304
4252
  `);
4305
4253
  });
@@ -4311,36 +4259,37 @@ program.command("login").description("Sign in by approving a browser code and co
4311
4259
  program.command("git-credential <operation>").description("Git credential helper using the saved login; invoked automatically by Git").hook("preAction", async (command) => {
4312
4260
  if (command.args[0] === "get") await requireLogin(command);
4313
4261
  }).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>", "SlateID, public hostname, or local (run from the checkout root)").description("Join a private slate you can view using ctx login, enabling member-scoped unattended work; returns slate and user IDs").hook("preAction", requireLogin).action(async (target) => {
4318
- await joinSlate(union([
4319
- literal("local"),
4320
- uuid(),
4321
- hostname()
4322
- ]).parse(target));
4323
- });
4324
- const connections = program.command("connections").description("Manage Owner-owned connected accounts or list app and agent connections and assignments; local reads only the current checkout and its saved development assignments").option("--org <slug>", "Manage this organization's connected accounts; defaults to the signed-in user").argument("[target]", "SlateID or public hostname (pushed requirements), or local (current checkout; no platform request)").addArgument(new Argument("[operation]", "Slate 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) => {
4327
- if (!target || !operation) connections.error("Expected: ctx connections <target> list");
4328
- await listSlateConnections(target);
4262
+ program.command("whoami").description("Show your ContextLayer user ID verified by the auth server, with the issuer and platform URL in use").action(async (_options, command) => {
4263
+ const { id } = await authenticate(command);
4264
+ console.log(`User: ${id}\nIssuer: ${env.CTX_AUTH_ISSUER}\nPlatform: ${env.CTX_PLATFORM_URL}`);
4329
4265
  });
4330
- connections.command("list").description("List the selected Owner's connected accounts with their toolkit, identity, and status").action(async (_options, command) => {
4331
- await listConnections(ownerOptionsSchema.parse(command.optsWithGlobals()).org);
4266
+ const accounts = program.command("accounts").description("Connect and list the selected Owner's connected accounts").option("--org <slug>", "Manage this organization's connected accounts; defaults to the signed-in user").hook("preAction", requireLogin);
4267
+ accounts.command("list").description("List the selected Owner's connected accounts with their toolkit, identity, and status").action(async (_options, command) => {
4268
+ await listAccounts(ownerOptionsSchema.parse(command.optsWithGlobals()).org);
4332
4269
  });
4333
- connections.command("add <toolkit>").description("Connect the selected Owner's account in your browser (for example, gmail), then assign the connected account to a slate's declared connection with ctx connection").action(async (toolkit, _options, command) => {
4270
+ accounts.command("add <toolkit>").description("Connect the selected Owner's account in your browser (for example, gmail), then assign it with ctx connections <target> assign <slug> [connection_id]").action(async (toolkit, _options, command) => {
4334
4271
  const options = ownerOptionsSchema.parse(command.optsWithGlobals());
4335
4272
  await connectAccount(string().min(1).parse(toolkit), options.org);
4336
4273
  });
4337
- program.command("connection").description("Assign a connected account to a declared connection: local saves a personal connected account assignment in .contextlayer/connections.json; SlateID/hostname saves a hosted assignment under its declared Owner").argument("<target>", "SlateID, public hostname, or local (run from the checkout root)").addArgument(new Argument("<operation>").choices(["assign"])).argument("<slug>", "Connection slug declared in slate.json (for example, work-email)").argument("[connection_id]", "Connected account ID; omit locally to assign a connected account for the declared toolkit interactively; required for hosted targets").hook("preAction", requireLogin).action(async (target, _operation, slug, connectionId) => {
4338
- const input = connectionAssignmentSchema.parse({
4274
+ program.command("connections").description("List declared app and agent connections or assign connected accounts; local reads and saves checkout configuration, SlateID/hostname uses pushed declarations and hosted assignments").argument("<target>", "SlateID, public hostname, or local (run from the checkout root)").addArgument(new Argument("<operation>").choices(["list", "assign"])).argument("[slug]", "Required for assign: connection slug declared in slate.json (for example, work-email)").argument("[connection_id]", "For assign: connected account ID; omit locally to choose interactively; required for hosted targets").action(async (target, operation, slug, connectionId, _options, command) => {
4275
+ const parsed = connectionsCommandSchema.safeParse({
4339
4276
  connection_id: connectionId,
4277
+ operation,
4340
4278
  slug,
4341
4279
  target
4342
4280
  });
4343
- await assignSlateConnection(input.target, input.slug, input.connection_id);
4281
+ if (!parsed.success) command.error(parsed.error.issues.map((issue) => issue.message).join("\n"));
4282
+ const input = parsed.data;
4283
+ if (input.operation !== "list" || input.target !== "local") await requireLogin(command);
4284
+ switch (input.operation) {
4285
+ case "list":
4286
+ await listSlateConnections(input.target);
4287
+ break;
4288
+ case "assign":
4289
+ await assignSlateConnection(input.target, input.slug, input.connection_id);
4290
+ break;
4291
+ default: throw new Error(`Unhandled connections operation: ${JSON.stringify(input)}`);
4292
+ }
4344
4293
  });
4345
4294
  program.command("agent").addArgument(new Argument("<target>", "Run from the checkout root").choices(["local"])).addArgument(new Argument("<operation>").choices(["run"])).argument("<name>", "Agent directory under agents/").argument("<prompt>", "Task to send as the user message").description("Run agents/<name>/instructions.md locally with Claude Agent SDK and native Claude authentication; Vite startup also requires ctx login when the app declares connections").action(async (_target, _operation, name, prompt) => {
4346
4295
  const input = object({
@@ -4359,6 +4308,15 @@ secrets.command("set").argument("<name>", "Secret name: uppercase letters, digit
4359
4308
  secrets.command("drop").argument("<name>", "Secret name: uppercase letters, digits, and underscores; cannot start with a digit").description("Delete the selected Owner's shared secret; returns only Owner and name").action(async (name, _options, command) => {
4360
4309
  await dropSecret(secretNameSchema.parse(name), secretOptionsSchema.parse(command.optsWithGlobals()).org);
4361
4310
  });
4311
+ program.command("db.codegen").argument("<slug>", "Database declared in public/slate.json or agents/<name>/slate.json").description("Generate databases/<slug>/types.ts from the migrated local database").addHelpText("after", "\nRun from the slate root with pnpm dev running.\nApply migrations with pnpm exec kysely before generating types.\n\nExample:\n pnpm db.codegen notes").action(async (slug, _options, command) => {
4312
+ const { generateDatabaseTypes } = await import("./database-codegen-C5TEhR5_.mjs");
4313
+ try {
4314
+ await generateDatabaseTypes(databaseSlugSchema.parse(slug));
4315
+ } catch (error) {
4316
+ if (error instanceof Error && "code" in error && error.code === "ENOENT" && "path" in error && error.path === resolve("public/slate.json")) command.error(`Missing ${error.path}. Run pnpm db.codegen from the slate root containing public/slate.json.`);
4317
+ throw error;
4318
+ }
4319
+ });
4362
4320
  await program.parseAsync();
4363
4321
  //#endregion
4364
4322
  export {};