@usecontextlayer/cli 0.13.0 → 0.14.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/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { _ as url, a as whoami, c as array, d as literal, f as looseObject, g as unknown, h as string, i as login, l as boolean, m as record, n as getAccessToken, o as env, p as object, r as getCurrentUser, s as _enum, t as getLocalAppId, u as hostname, v as uuid, y as execa } from "./app-CRgSQabE.mjs";
2
+ import { S as execa, _ as record, a as getAccessToken, b as url, c as whoami, d as boolean, f as discriminatedUnion, g as object, h as looseObject, i as secretNameSchema, l as env, m as literal, n as readLocalManifest, o as getCurrentUser, p as hostname, r as credentialModeSchema, s as login, t as getLocalAppId, u as array, v as string, x as uuid, y as unknown } from "./app-DUUpiXEm.mjs";
3
3
  import { cp, readFile, writeFile } from "node:fs/promises";
4
4
  import path, { join } from "node:path";
5
5
  import { EventEmitter } from "node:events";
@@ -3004,7 +3004,34 @@ async function runLocalAgent(name, prompt) {
3004
3004
  }
3005
3005
  }
3006
3006
  //#endregion
3007
+ //#region owner.ts
3008
+ const ownerOptionsSchema = object({ org: string().min(1).optional() });
3009
+ async function resolveOwner(org, headers) {
3010
+ if (!org) return {
3011
+ id: (await getCurrentUser(headers)).id,
3012
+ type: "user"
3013
+ };
3014
+ const response = await fetch(new URL("/api/platform/organizations", env.CTX_AUTH_ISSUER), { headers });
3015
+ if (!response.ok) throw new Error(`Organization lookup failed: HTTP ${response.status}`);
3016
+ const { organizations } = object({ organizations: array(object({
3017
+ id: string().min(1),
3018
+ slug: string()
3019
+ })) }).parse(await response.json());
3020
+ const organization = organizations.find((organization) => organization.slug === org);
3021
+ if (!organization) throw new Error("Organization is not in your memberships");
3022
+ return {
3023
+ id: organization.id,
3024
+ type: "organization"
3025
+ };
3026
+ }
3027
+ function ownerPath(owner) {
3028
+ return `${owner.type === "organization" ? "org" : "user"}/${encodeURIComponent(owner.id)}`;
3029
+ }
3030
+ //#endregion
3007
3031
  //#region connections.ts
3032
+ async function getLocalManifest(target) {
3033
+ return target === "local" ? readLocalManifest() : void 0;
3034
+ }
3008
3035
  async function resolveAppTarget(target) {
3009
3036
  if (target !== "local") return target;
3010
3037
  return getLocalAppId();
@@ -3014,6 +3041,7 @@ async function assignAppConnection(target, slug, connectionId) {
3014
3041
  const response = await fetch(new URL(`/api/apps/${encodeURIComponent(appTarget)}/connections/assign`, env.CTX_PLATFORM_URL), {
3015
3042
  body: JSON.stringify({
3016
3043
  connection_id: connectionId,
3044
+ manifest: await getLocalManifest(target),
3017
3045
  slug
3018
3046
  }),
3019
3047
  headers: {
@@ -3031,7 +3059,7 @@ async function assignAppConnection(target, slug, connectionId) {
3031
3059
  }
3032
3060
  async function listAppConnections(target) {
3033
3061
  const appTarget = await resolveAppTarget(target);
3034
- const manifest = target === "local" ? JSON.parse(await readFile("public/slate.json", "utf8")) : void 0;
3062
+ const manifest = await getLocalManifest(target);
3035
3063
  const response = await fetch(new URL(`/api/apps/${encodeURIComponent(appTarget)}/connections/list`, env.CTX_PLATFORM_URL), {
3036
3064
  body: JSON.stringify({ manifest }),
3037
3065
  headers: {
@@ -3043,7 +3071,7 @@ async function listAppConnections(target) {
3043
3071
  if (!response.ok) throw new Error(`App connections request failed: HTTP ${response.status}`);
3044
3072
  const { connections } = object({ connections: array(object({
3045
3073
  connection_id: string().nullable(),
3046
- mode: _enum(["individual", "shared"]),
3074
+ mode: credentialModeSchema,
3047
3075
  slug: string(),
3048
3076
  toolkit: string()
3049
3077
  })) }).parse(await response.json());
@@ -3056,22 +3084,26 @@ async function listAppConnections(target) {
3056
3084
  connection: connection_id ?? "unassigned"
3057
3085
  })));
3058
3086
  }
3059
- async function addConnection(toolkit) {
3060
- const response = await fetch(new URL("/api/connections", env.CTX_PLATFORM_URL), {
3087
+ async function addConnection(toolkit, org) {
3088
+ const headers = { authorization: `Bearer ${await getAccessToken()}` };
3089
+ const owner = await resolveOwner(org, headers);
3090
+ const response = await fetch(new URL(`/api/connections/${ownerPath(owner)}`, env.CTX_PLATFORM_URL), {
3061
3091
  body: JSON.stringify({ toolkit }),
3062
3092
  headers: {
3063
- authorization: `Bearer ${await getAccessToken()}`,
3093
+ ...headers,
3064
3094
  "content-type": "application/json"
3065
3095
  },
3066
3096
  method: "POST"
3067
3097
  });
3068
3098
  if (!response.ok) throw new Error(`Connection authorization failed: HTTP ${response.status}`);
3069
3099
  const { redirect_url } = object({ redirect_url: url() }).parse(await response.json());
3070
- console.log(`Open this link to connect your account:\n${redirect_url}`);
3071
- console.log("After authorization, run ctx connections list to see your account.");
3100
+ console.log(`Open this link to authorize the Owner's account:\n${redirect_url}`);
3101
+ console.log(`After authorization, run ctx connections${org ? ` --org ${org}` : ""} list to see the Owner's account.`);
3072
3102
  }
3073
- async function listConnections() {
3074
- const response = await fetch(new URL("/api/connections", env.CTX_PLATFORM_URL), { headers: { authorization: `Bearer ${await getAccessToken()}` } });
3103
+ async function listConnections(org) {
3104
+ const headers = { authorization: `Bearer ${await getAccessToken()}` };
3105
+ const owner = await resolveOwner(org, headers);
3106
+ const response = await fetch(new URL(`/api/connections/${ownerPath(owner)}`, env.CTX_PLATFORM_URL), { headers });
3075
3107
  if (!response.ok) throw new Error(`Connection list failed: HTTP ${response.status}`);
3076
3108
  const { connections } = object({ connections: array(object({
3077
3109
  alias: string().nullable(),
@@ -3128,7 +3160,7 @@ async function gitCredential(operation) {
3128
3160
  }
3129
3161
  //#endregion
3130
3162
  //#region package.json
3131
- var version = "0.13.0";
3163
+ var version = "0.14.0";
3132
3164
  //#endregion
3133
3165
  //#region ../../node_modules/.pnpm/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/utils.js
3134
3166
  const getCodePointsLength = (() => {
@@ -3819,40 +3851,29 @@ ${r ? styleText("cyan", S_BAR_END) : ""}
3819
3851
  `${styleText("dim", "↑/↓")}`, `${styleText("dim", "Enter:")}`;
3820
3852
  `${styleText("gray", S_BAR)}`;
3821
3853
  //#endregion
3822
- //#region secrets.ts
3823
- const secretNameSchema = string().regex(/^[A-Z_][A-Z0-9_]*$/);
3824
- const secretOptionsSchema = object({
3825
- org: string().min(1).optional(),
3826
- stdin: boolean().default(false)
3854
+ //#region ../shared/dist/app-usage.mjs
3855
+ const ownerSchema = discriminatedUnion("type", [object({
3856
+ id: string().min(1),
3857
+ type: literal("user")
3858
+ }), object({
3859
+ id: string().min(1),
3860
+ type: literal("organization")
3861
+ })]);
3862
+ object({
3863
+ id: uuid(),
3864
+ owner: ownerSchema
3827
3865
  });
3866
+ //#endregion
3867
+ //#region secrets.ts
3868
+ const secretOptionsSchema = ownerOptionsSchema.extend({ stdin: boolean().default(false) });
3828
3869
  const valueSchema = string().min(1);
3829
3870
  const confirmationSchema = object({
3830
3871
  name: secretNameSchema,
3831
- owner: object({
3832
- id: string().min(1),
3833
- type: _enum(["organization", "user"])
3834
- })
3872
+ owner: ownerSchema
3835
3873
  });
3836
3874
  async function resolveSecretUrl(name, org, headers) {
3837
- let type;
3838
- let id;
3839
- if (org) {
3840
- const url = new URL("/api/platform/organizations", env.CTX_AUTH_ISSUER);
3841
- const response = await fetch(url, { headers });
3842
- if (!response.ok) throw new Error(`Organization lookup failed: HTTP ${response.status}`);
3843
- const { organizations } = object({ organizations: array(object({
3844
- id: string().min(1),
3845
- slug: string()
3846
- })) }).parse(await response.json());
3847
- const organization = organizations.find((organization) => organization.slug === org);
3848
- if (!organization) throw new Error("Organization is not in your memberships");
3849
- type = "org";
3850
- id = organization.id;
3851
- } else {
3852
- id = (await getCurrentUser(headers)).id;
3853
- type = "user";
3854
- }
3855
- return new URL(`/api/secrets/${type}/${encodeURIComponent(id)}/${encodeURIComponent(name)}`, env.CTX_PLATFORM_URL);
3875
+ const owner = await resolveOwner(org, headers);
3876
+ return new URL(`/api/secrets/${ownerPath(owner)}/${encodeURIComponent(name)}`, env.CTX_PLATFORM_URL);
3856
3877
  }
3857
3878
  async function setSecret(name, options) {
3858
3879
  const headers = { authorization: `Bearer ${await getAccessToken()}` };
@@ -3997,13 +4018,18 @@ program.command("login").description("Sign in by approving a browser code and co
3997
4018
  });
3998
4019
  program.command("git-credential <operation>").description("Git credential helper using the saved login; invoked automatically by Git").action(gitCredential);
3999
4020
  program.command("whoami").description("Show your ContextLayer user ID using the saved login").action(whoami);
4000
- const connections = program.command("connections").description("Manage your connected accounts").argument("[target]", "AppID, public hostname, or local (run from the app root)").addArgument(new Argument("[operation]", "App connection operation").choices(["list"])).action(async (target, operation) => {
4021
+ const connections = program.command("connections").description("Manage Owner-owned connected accounts or list an app's requirements").option("--org <org_slug>", "Manage this organization's connections; defaults to the signed-in user").argument("[target]", "AppID, public hostname, or local (run from the app root)").addArgument(new Argument("[operation]", "App connection operation").choices(["list"])).action(async (target, operation) => {
4001
4022
  if (!target || !operation) connections.error("Expected: ctx connections <target> list");
4002
4023
  await listAppConnections(target);
4003
4024
  });
4004
- connections.command("list").description("List your Composio accounts with their toolkit, identity, and status").action(listConnections);
4005
- connections.command("add <toolset>").description("Print a Composio authorization link to connect an account (for example, gmail)").action(addConnection);
4006
- program.command("connection").description("Assign a connection to an app requirement").argument("<target>", "AppID, public hostname, or local (run from the app root)").addArgument(new Argument("<operation>").choices(["assign"])).argument("<slug>", "Connection requirement slug").argument("<connection_id>", "Composio connected-account ID").action(async (target, _operation, slug, connectionId) => {
4025
+ connections.command("list").description("List the selected Owner's Composio accounts with their toolkit, identity, and status").action(async (_options, command) => {
4026
+ await listConnections(ownerOptionsSchema.parse(command.optsWithGlobals()).org);
4027
+ });
4028
+ connections.command("add <toolkit>").description("Print a Composio authorization link for the selected Owner's account (for example, gmail)").action(async (toolkit, _options, command) => {
4029
+ const options = ownerOptionsSchema.parse(command.optsWithGlobals());
4030
+ await addConnection(string().min(1).parse(toolkit), options.org);
4031
+ });
4032
+ program.command("connection").description("Assign a connection using the requirement’s viewer or owner mode; owner mode requires app management").argument("<target>", "AppID, public hostname, or local (run from the app root)").addArgument(new Argument("<operation>").choices(["assign"])).argument("<slug>", "Connection requirement slug").argument("<connection_id>", "Composio connected-account ID").action(async (target, _operation, slug, connectionId) => {
4007
4033
  await assignAppConnection(target, slug, connectionId);
4008
4034
  });
4009
4035
  program.command("agent").addArgument(new Argument("<target>", "Run from the app 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; no platform server or ctx login required").action(async (_target, _operation, name, prompt) => {