Skip to content

feat(cli): transcend custom-functions push and list commands - #322

Open
ecton-transcend wants to merge 6 commits into
ecton-custom-functions-sdkfrom
ecton-custom-functions-cli
Open

feat(cli): transcend custom-functions push and list commands#322
ecton-transcend wants to merge 6 commits into
ecton-custom-functions-sdkfrom
ecton-custom-functions-cli

Conversation

@ecton-transcend

@ecton-transcend ecton-transcend commented Jul 20, 2026

Copy link
Copy Markdown

Related Issues

Public Changelog

  • Added: transcend custom-functions push — create and update Transcend custom function code revisions from a manifest file (transcend-functions.yml) in your own repository, designed to run on every CI push. Supports change detection (unchanged functions are skipped), draft + promote flows (--noPromote), --dryRun previews, --force for env-value rotations, --sombraAuth for self-hosted Sombra gateways, per-function Sombra gateways (sombra-id), and --updateManifest to record assigned function IDs back into the manifest.
  • Added: transcend custom-functions list — list custom functions with their lifecycle state, active version, pending draft, and IDs.

Internal Changelog

  • Added: manifest codec (io-ts) with variable templating and code-file loading, plus comment-preserving YAML write-back of assigned custom function IDs.

Part 2 of 3, stacked on #321 (the SDK helpers). Retarget to main after #321 merges.

Implementation details

Behavior

  • push reads the manifest, signs each function's code against the customer ingress of the Sombra gateway the function belongs to (/v1/custom/sign), and saves the pre-signed JWTs via the GraphQL mutations. Entries are matched by id first when set, falling back to exact name; ambiguous names fail with an error listing candidate IDs.
  • Functions in one manifest may belong to different Sombra gateways: the gateway per entry resolves as manifest sombra-id → existing function's gateway → --sombraId → primary, with one cached customer-ingress connection per distinct gateway. An entry that pins a different gateway than the existing function fails (a push cannot move functions between gateways). --sombraAuth provides the default internal key; entries whose self-hosted gateway uses a different key set sombra-auth-env to the name of an environment variable holding that gateway's key (the key itself never lives in the manifest).
  • --sombraAuth takes the Sombra internal key (X-Sombra-Authorization), required when self-hosting Sombra — the same flag pattern as the existing request commands.
  • --updateManifest writes assigned IDs back into the manifest via a comment-preserving YAML document edit (new yaml dependency, added to the pnpm catalog), so <<parameters.x>> secret placeholders and comments survive.
  • list prints each function's ID, type, lifecycle state, active version, and pending draft.

Key files

  • CLI command: packages/cli/src/commands/custom-functions/push/impl.ts
  • Manifest codec + ID write-back: packages/cli/src/lib/custom-functions/manifest.ts

Dependencies / rollout

  • Requires the Sombra customer-ingress /v1/custom/sign route from the backend pivot (#46175); --dryRun and list work without it.
  • A composite GitHub Action wrapping this command lands in the stacked feat(actions): Add Github actions package #323.

Testing

  • Unit tests cover manifest read (ID/name validation, duplicate errors) and ID write-back (comment + placeholder preservation); per-gateway resolution rules are tested in the SDK; 215+ CLI tests pass overall.

@ecton-transcend ecton-transcend self-assigned this Jul 20, 2026
@linear-code

linear-code Bot commented Jul 20, 2026

Copy link
Copy Markdown

WAL-9270

@pkg-pr-new

pkg-pr-new Bot commented Jul 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

@transcend-io/airgap.js-types

pnpm add https://pkg.pr.new/@transcend-io/airgap.js-types@322
yarn add https://pkg.pr.new/@transcend-io/airgap.js-types@322.tgz

@transcend-io/cli

pnpm add https://pkg.pr.new/@transcend-io/cli@322
yarn add https://pkg.pr.new/@transcend-io/cli@322.tgz

@transcend-io/design-tokens

pnpm add https://pkg.pr.new/@transcend-io/design-tokens@322
yarn add https://pkg.pr.new/@transcend-io/design-tokens@322.tgz

@transcend-io/internationalization

pnpm add https://pkg.pr.new/@transcend-io/internationalization@322
yarn add https://pkg.pr.new/@transcend-io/internationalization@322.tgz

@transcend-io/privacy-types

pnpm add https://pkg.pr.new/@transcend-io/privacy-types@322
yarn add https://pkg.pr.new/@transcend-io/privacy-types@322.tgz

@transcend-io/sdk

pnpm add https://pkg.pr.new/@transcend-io/sdk@322
yarn add https://pkg.pr.new/@transcend-io/sdk@322.tgz

@transcend-io/type-utils

pnpm add https://pkg.pr.new/@transcend-io/type-utils@322
yarn add https://pkg.pr.new/@transcend-io/type-utils@322.tgz

@transcend-io/utils

pnpm add https://pkg.pr.new/@transcend-io/utils@322
yarn add https://pkg.pr.new/@transcend-io/utils@322.tgz

@transcend-io/mcp

pnpm add https://pkg.pr.new/@transcend-io/mcp@322
yarn add https://pkg.pr.new/@transcend-io/mcp@322.tgz

@transcend-io/mcp-server-admin

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-admin@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-admin@322.tgz

@transcend-io/mcp-server-assessment

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-assessment@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-assessment@322.tgz

@transcend-io/mcp-server-base

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-base@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-base@322.tgz

@transcend-io/mcp-server-consent

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-consent@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-consent@322.tgz

@transcend-io/mcp-server-discovery

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-discovery@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-discovery@322.tgz

@transcend-io/mcp-server-docs

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-docs@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-docs@322.tgz

@transcend-io/mcp-server-dsr

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-dsr@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-dsr@322.tgz

@transcend-io/mcp-server-inventory

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-inventory@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-inventory@322.tgz

@transcend-io/mcp-server-preferences

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-preferences@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-preferences@322.tgz

@transcend-io/mcp-server-workflows

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-workflows@322
yarn add https://pkg.pr.new/@transcend-io/mcp-server-workflows@322.tgz

commit: 7dcefc1

@ecton-transcend
ecton-transcend marked this pull request as ready for review July 20, 2026 04:27
@ecton-transcend

Copy link
Copy Markdown
Author

🤖 Posted by Cursor on behalf of @ecton-transcend

Automated Change: Auth pivot to customer-ingress signing

Per security review feedback on the platform side (#46175), the signing flow no longer exchanges the API key for a Sombra employee session over a Diffie-Hellman channel. The CLI now signs code directly against the Sombra customer-ingress /v1/custom/sign route (bearer token auth) and saves the pre-signed JWTs via GraphQL.

Implementation details

What changed: All DH crypto and the session-exchange helper were removed from the SDK; a signCustomFunctionCode customer-ingress client and resolveSombraCustomerUrl helper were added; the GraphQL mutations now receive signedCodeJwt / signedCodeContextJwt inside input instead of dhEncrypted; the CLI gained --sombraAuth (Sombra internal key, needed when self-hosting) and the action a matching sombra-auth input.
Why: The employee-session exchange hard-coded SombraEmployeeAuthMethod.Transcend, which is not enabled on all self-hosted gateways; the customer-ingress bearer token is the established auth model for internal routes.
Impact: Same trust model at rest (backend stores unverifiable HMAC JWTs either way); code/env plaintext now travels only over TLS to the customer's own gateway. Requires a Sombra version with the new route — older gateways get a friendly upgrade error.

@ecton-transcend

Copy link
Copy Markdown
Author

🤖 Posted by Cursor on behalf of @ecton-transcend

Automated Change: Per-gateway code signing

Custom functions can belong to different Sombra gateways, and a function's JWTs must be signed by its own gateway's keys or they fail verification at execution time. Previously the push signed every function against a single gateway (the --sombraId flag or the primary), which could silently produce runtime-broken revisions for functions on other gateways.

Implementation details

What changed: The customFunctions query now fetches each function's sombraId; new resolveEffectiveSombraId resolves the signing gateway per function (manifest sombra-id → existing function's gateway → --sombraId → primary) and rejects configs that try to move an existing function between gateways; the CLI caches one customer-ingress connection per distinct gateway and signs each function against its own.
Why: Signing against the wrong gateway succeeds at push time (the backend cannot verify the HMAC JWTs) and only fails at execution — the worst failure mode.
Impact: Single-gateway orgs are unaffected (everything resolves to the primary). Multi-gateway manifests now work in one push, with the documented limitation that one --sombraAuth key applies to all gateways in a run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant