replicas-cli 0.2.715 → 0.2.717

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 (2) hide show
  1. package/dist/index.cjs +48 -49
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -27439,7 +27439,7 @@ var headlessFilesystemAgentResultSchema = external_exports.object({
27439
27439
  });
27440
27440
 
27441
27441
  // ../shared/src/cli-version.ts
27442
- var CLI_VERSION = "0.2.715";
27442
+ var CLI_VERSION = "0.2.717";
27443
27443
 
27444
27444
  // ../shared/src/version.ts
27445
27445
  function compareVersions(v1, v2) {
@@ -28105,15 +28105,17 @@ async function readSseStream(body, onEvent, shouldContinue = () => true) {
28105
28105
  var import_yaml2 = __toESM(require_dist());
28106
28106
 
28107
28107
  // ../shared/src/workspace-identity.ts
28108
- var WORKSPACE_IDENTITY_AUDIENCE = "replicas:workspace";
28109
28108
  var WORKSPACE_TOKEN_TTL_SECONDS = 15 * 60;
28110
- var WORKSPACE_TOKEN_FILE = `${SANDBOX_PATHS.REPLICAS_DIR}/credentials/tokens.json`;
28109
+ var IDENTITY_PROXY_PORT = 17323;
28110
+ var IDENTITY_TOKEN_PATH = "/token";
28111
28111
  var WORKSPACE_TOKEN_ENV = {
28112
28112
  TOKEN: "REPLICAS_WORKSPACE_TOKEN",
28113
- TOKEN_FILE: "REPLICAS_WORKSPACE_TOKEN_FILE",
28114
- AUDIENCE: "REPLICAS_WORKSPACE_TOKEN_AUDIENCE",
28115
- ISSUER: "REPLICAS_WORKSPACE_IDENTITY_ISSUER"
28113
+ AUDIENCE: "REPLICAS_WORKSPACE_TOKEN_AUDIENCE"
28116
28114
  };
28115
+ var workspaceAudienceSchema = external_exports.string().min(1).max(255).regex(/^[\x21-\x7e]+$/, "Audience must be a single printable token without spaces");
28116
+ var workspaceTokenRequestSchema = external_exports.object({
28117
+ audience: workspaceAudienceSchema.optional()
28118
+ });
28117
28119
  var workspaceTokenClaimsSchema = external_exports.object({
28118
28120
  iss: external_exports.string(),
28119
28121
  sub: external_exports.string(),
@@ -28125,31 +28127,27 @@ var workspaceTokenClaimsSchema = external_exports.object({
28125
28127
  organization_id: external_exports.string().uuid(),
28126
28128
  workspace_id: external_exports.string().uuid(),
28127
28129
  environment_id: external_exports.string().uuid(),
28128
- creator_id: external_exports.string().uuid().nullable()
28130
+ creator_id: external_exports.string().uuid().nullable(),
28131
+ creator_email: external_exports.string().nullable().default(null)
28129
28132
  }).passthrough();
28130
- var workspaceTokenBundleSchema = external_exports.strictObject({
28133
+ var workspaceTokenResponseSchema = external_exports.object({
28134
+ issuer: external_exports.string(),
28135
+ audience: external_exports.string(),
28136
+ token: external_exports.string(),
28137
+ expires_at: external_exports.string()
28138
+ });
28139
+ var legacyWorkspaceTokenBundleSchema = external_exports.object({
28131
28140
  version: external_exports.literal(1),
28132
28141
  issuer: external_exports.string(),
28133
28142
  default_audience: external_exports.string(),
28134
- tokens: external_exports.record(
28135
- external_exports.string(),
28136
- external_exports.strictObject({ token: external_exports.string(), expires_at: external_exports.string() })
28137
- )
28138
- });
28139
- var workloadAudienceInput = external_exports.strictObject({
28140
- name: external_exports.string().trim().min(1).max(64),
28141
- audience: external_exports.string().min(1).max(255).regex(/^[\x21-\x7e]+$/, "Audience must be a single printable token without spaces").refine((value) => value !== WORKSPACE_IDENTITY_AUDIENCE, "Reserved audience"),
28142
- enabled: external_exports.boolean(),
28143
- is_default: external_exports.boolean()
28143
+ tokens: external_exports.record(external_exports.string(), external_exports.object({ token: external_exports.string(), expires_at: external_exports.string() }))
28144
28144
  });
28145
28145
  var mcpWorkloadAuthSchema = external_exports.strictObject({
28146
28146
  type: external_exports.literal("workspace_identity"),
28147
- audience: external_exports.string().min(1)
28147
+ audience: workspaceAudienceSchema
28148
28148
  });
28149
- function currentWorkspaceToken(bundle, audience = bundle.default_audience, now = Date.now()) {
28150
- const entry = bundle.tokens[audience];
28151
- if (!entry || Date.parse(entry.expires_at) <= now) return null;
28152
- return entry.token;
28149
+ function identityTokenUrl() {
28150
+ return `http://127.0.0.1:${IDENTITY_PROXY_PORT}${IDENTITY_TOKEN_PATH}`;
28153
28151
  }
28154
28152
 
28155
28153
  // src/lib/monolith-url.ts
@@ -28653,26 +28651,27 @@ var import_node_child_process = require("child_process");
28653
28651
  var import_node_os = require("os");
28654
28652
  var import_node_path = require("path");
28655
28653
  var import_chalk5 = __toESM(require("chalk"));
28656
- function readToken(audience) {
28657
- const file2 = process.env[WORKSPACE_TOKEN_ENV.TOKEN_FILE] ?? WORKSPACE_TOKEN_FILE;
28658
- let bundle;
28654
+ async function fetchToken(audience) {
28655
+ let response;
28659
28656
  try {
28660
- bundle = workspaceTokenBundleSchema.parse(JSON.parse((0, import_node_fs.readFileSync)(file2, "utf8")));
28661
- } catch {
28662
- return createErrorResult({ message: `No valid workspace token bundle at ${file2}. Check that workspace identity is enabled.` });
28663
- }
28664
- const selected = audience ?? bundle.default_audience;
28665
- const token = currentWorkspaceToken(bundle, selected);
28666
- const expiresAt = Date.parse(bundle.tokens[selected]?.expires_at);
28667
- if (!token || !Number.isFinite(expiresAt)) {
28668
- return createErrorResult({
28669
- message: bundle.tokens[selected] ? `The workspace token for ${selected} has expired or has an invalid expiry and has not been renewed yet.` : `No workspace token is issued for audience ${selected}. Registered: ${Object.keys(bundle.tokens).join(", ")}`
28657
+ response = await fetch(identityTokenUrl(), {
28658
+ method: "POST",
28659
+ headers: { "content-type": "application/json" },
28660
+ body: JSON.stringify(audience ? { audience } : {})
28670
28661
  });
28662
+ } catch {
28663
+ return createErrorResult({ message: "Workspace identity is only available inside a running Replicas workspace." });
28664
+ }
28665
+ const body = await response.json().catch(() => null);
28666
+ const parsed = workspaceTokenResponseSchema.safeParse(body);
28667
+ if (!response.ok || !parsed.success) {
28668
+ const detail = isRecord(body) && typeof body.error === "string" ? body.error : `HTTP ${response.status}`;
28669
+ return createErrorResult({ message: `Could not obtain a workspace token: ${detail}` });
28671
28670
  }
28672
- return createSuccessResult({ token, audience: selected, expiresAt });
28671
+ return createSuccessResult(parsed.data);
28673
28672
  }
28674
- function identityTokenCommand(options) {
28675
- const result = readToken(options.audience);
28673
+ async function identityTokenCommand(options) {
28674
+ const result = await fetchToken(options.audience);
28676
28675
  if (!result.ok) {
28677
28676
  if (options.format === "gcp") {
28678
28677
  process.stdout.write(`${JSON.stringify({ version: 1, success: false, code: "TOKEN_UNAVAILABLE", message: result.error.message })}
@@ -28683,23 +28682,23 @@ function identityTokenCommand(options) {
28683
28682
  process.exitCode = 1;
28684
28683
  return;
28685
28684
  }
28686
- const { token, expiresAt } = result.data;
28685
+ const { token, expires_at } = result.data;
28687
28686
  const output = options.format === "gcp" ? JSON.stringify({
28688
28687
  version: 1,
28689
28688
  success: true,
28690
28689
  token_type: "urn:ietf:params:oauth:token-type:jwt",
28691
28690
  id_token: token,
28692
- expiration_time: Math.floor(expiresAt / 1e3)
28691
+ expiration_time: Math.floor(Date.parse(expires_at) / 1e3)
28693
28692
  }) : token;
28694
28693
  process.stdout.write(`${output}
28695
28694
  `);
28696
28695
  }
28697
- function identityExecCommand(command, options) {
28696
+ async function identityExecCommand(command, options) {
28698
28697
  if (command.length === 0) {
28699
28698
  console.error(import_chalk5.default.red("Provide a command to run: replicas identity exec -- <command>"));
28700
28699
  process.exit(1);
28701
28700
  }
28702
- const tokenResult = readToken(options.audience);
28701
+ const tokenResult = await fetchToken(options.audience);
28703
28702
  if (!tokenResult.ok) {
28704
28703
  console.error(import_chalk5.default.red(tokenResult.error.message));
28705
28704
  process.exitCode = 1;
@@ -28712,8 +28711,8 @@ function identityExecCommand(command, options) {
28712
28711
  });
28713
28712
  process.exit(result.status ?? 1);
28714
28713
  }
28715
- function identityAwsCredentialsCommand(options) {
28716
- const result = readToken(options.audience);
28714
+ async function identityAwsCredentialsCommand(options) {
28715
+ const result = await fetchToken(options.audience);
28717
28716
  if (!result.ok) {
28718
28717
  console.error(import_chalk5.default.red(result.error.message));
28719
28718
  process.exitCode = 1;
@@ -33109,10 +33108,10 @@ if (isAgentMode()) {
33109
33108
  });
33110
33109
  }
33111
33110
  if (isAgentMode()) {
33112
- const identity = program.command("identity").description("Read the short-lived workspace identity token delivered by Replicas");
33113
- identity.command("token").description("Print the current workspace token for an audience (defaults to the environment default audience)").option("-a, --audience <audience>", "Registered audience to read the token for").addOption(new import_commander.Option("--format <format>", "Output a raw token or Google executable credentials").choices(["text", "gcp"]).default("text")).action(identityTokenCommand);
33114
- identity.command("aws-credentials").description("Exchange the current token for temporary AWS credentials (for AWS credential_process)").requiredOption("--role-arn <arn>", "IAM role that trusts the Replicas issuer").option("-a, --audience <audience>", "Registered audience accepted by the IAM OIDC provider", "sts.amazonaws.com").option("--region <region>", "AWS region for the STS exchange").option("--session-name <name>", "AWS role session name", "replicas").action(identityAwsCredentialsCommand);
33115
- identity.command("exec [command...]").description("Run a command with REPLICAS_WORKSPACE_TOKEN set to a fresh token: replicas identity exec -- curl ...").option("-a, --audience <audience>", "Registered audience to read the token for").action((command, options) => identityExecCommand(command, options));
33111
+ const identity = program.command("identity").description("Obtain short-lived workspace identity tokens (OIDC) for the services this workspace calls");
33112
+ identity.command("token").description("Print a workspace token for an audience (defaults to replicas:workspace)").option("-a, --audience <audience>", "Audience (`aud`) the receiving service verifies, for example its URL").addOption(new import_commander.Option("--format <format>", "Output a raw token or Google executable credentials").choices(["text", "gcp"]).default("text")).action(identityTokenCommand);
33113
+ identity.command("aws-credentials").description("Exchange the current token for temporary AWS credentials (for AWS credential_process)").requiredOption("--role-arn <arn>", "IAM role that trusts the Replicas issuer").option("-a, --audience <audience>", "Audience configured on the IAM OIDC provider", "sts.amazonaws.com").option("--region <region>", "AWS region for the STS exchange").option("--session-name <name>", "AWS role session name", "replicas").action(identityAwsCredentialsCommand);
33114
+ identity.command("exec [command...]").description("Run a command with REPLICAS_WORKSPACE_TOKEN set to a fresh token: replicas identity exec -- curl ...").option("-a, --audience <audience>", "Audience (`aud`) the receiving service verifies").action((command, options) => identityExecCommand(command, options));
33116
33115
  const service = program.command("service").description("Run long-lived services (dev servers, daemons) detached from the agent session so they survive workspace sleep/wake");
33117
33116
  service.command("start <name> [command...]").description('Start (or restart) a named service as a detached daemon. Quote shell operators: replicas service start web "cd app && bun dev"').option("-d, --cwd <dir>", "Working directory for the service (defaults to the current directory)").action(async (name, commandParts, options) => {
33118
33117
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.715",
3
+ "version": "0.2.717",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {