replicas-cli 0.2.715 → 0.2.716

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 +46 -48
  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.716";
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(),
@@ -28127,29 +28129,24 @@ var workspaceTokenClaimsSchema = external_exports.object({
28127
28129
  environment_id: external_exports.string().uuid(),
28128
28130
  creator_id: external_exports.string().uuid().nullable()
28129
28131
  }).passthrough();
28130
- var workspaceTokenBundleSchema = external_exports.strictObject({
28132
+ var workspaceTokenResponseSchema = external_exports.object({
28133
+ issuer: external_exports.string(),
28134
+ audience: external_exports.string(),
28135
+ token: external_exports.string(),
28136
+ expires_at: external_exports.string()
28137
+ });
28138
+ var legacyWorkspaceTokenBundleSchema = external_exports.object({
28131
28139
  version: external_exports.literal(1),
28132
28140
  issuer: external_exports.string(),
28133
28141
  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()
28142
+ tokens: external_exports.record(external_exports.string(), external_exports.object({ token: external_exports.string(), expires_at: external_exports.string() }))
28144
28143
  });
28145
28144
  var mcpWorkloadAuthSchema = external_exports.strictObject({
28146
28145
  type: external_exports.literal("workspace_identity"),
28147
- audience: external_exports.string().min(1)
28146
+ audience: workspaceAudienceSchema
28148
28147
  });
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;
28148
+ function identityTokenUrl() {
28149
+ return `http://127.0.0.1:${IDENTITY_PROXY_PORT}${IDENTITY_TOKEN_PATH}`;
28153
28150
  }
28154
28151
 
28155
28152
  // src/lib/monolith-url.ts
@@ -28653,26 +28650,27 @@ var import_node_child_process = require("child_process");
28653
28650
  var import_node_os = require("os");
28654
28651
  var import_node_path = require("path");
28655
28652
  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;
28653
+ async function fetchToken(audience) {
28654
+ let response;
28659
28655
  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(", ")}`
28656
+ response = await fetch(identityTokenUrl(), {
28657
+ method: "POST",
28658
+ headers: { "content-type": "application/json" },
28659
+ body: JSON.stringify(audience ? { audience } : {})
28670
28660
  });
28661
+ } catch {
28662
+ return createErrorResult({ message: "Workspace identity is only available inside a running Replicas workspace." });
28663
+ }
28664
+ const body = await response.json().catch(() => null);
28665
+ const parsed = workspaceTokenResponseSchema.safeParse(body);
28666
+ if (!response.ok || !parsed.success) {
28667
+ const detail = isRecord(body) && typeof body.error === "string" ? body.error : `HTTP ${response.status}`;
28668
+ return createErrorResult({ message: `Could not obtain a workspace token: ${detail}` });
28671
28669
  }
28672
- return createSuccessResult({ token, audience: selected, expiresAt });
28670
+ return createSuccessResult(parsed.data);
28673
28671
  }
28674
- function identityTokenCommand(options) {
28675
- const result = readToken(options.audience);
28672
+ async function identityTokenCommand(options) {
28673
+ const result = await fetchToken(options.audience);
28676
28674
  if (!result.ok) {
28677
28675
  if (options.format === "gcp") {
28678
28676
  process.stdout.write(`${JSON.stringify({ version: 1, success: false, code: "TOKEN_UNAVAILABLE", message: result.error.message })}
@@ -28683,23 +28681,23 @@ function identityTokenCommand(options) {
28683
28681
  process.exitCode = 1;
28684
28682
  return;
28685
28683
  }
28686
- const { token, expiresAt } = result.data;
28684
+ const { token, expires_at } = result.data;
28687
28685
  const output = options.format === "gcp" ? JSON.stringify({
28688
28686
  version: 1,
28689
28687
  success: true,
28690
28688
  token_type: "urn:ietf:params:oauth:token-type:jwt",
28691
28689
  id_token: token,
28692
- expiration_time: Math.floor(expiresAt / 1e3)
28690
+ expiration_time: Math.floor(Date.parse(expires_at) / 1e3)
28693
28691
  }) : token;
28694
28692
  process.stdout.write(`${output}
28695
28693
  `);
28696
28694
  }
28697
- function identityExecCommand(command, options) {
28695
+ async function identityExecCommand(command, options) {
28698
28696
  if (command.length === 0) {
28699
28697
  console.error(import_chalk5.default.red("Provide a command to run: replicas identity exec -- <command>"));
28700
28698
  process.exit(1);
28701
28699
  }
28702
- const tokenResult = readToken(options.audience);
28700
+ const tokenResult = await fetchToken(options.audience);
28703
28701
  if (!tokenResult.ok) {
28704
28702
  console.error(import_chalk5.default.red(tokenResult.error.message));
28705
28703
  process.exitCode = 1;
@@ -28712,8 +28710,8 @@ function identityExecCommand(command, options) {
28712
28710
  });
28713
28711
  process.exit(result.status ?? 1);
28714
28712
  }
28715
- function identityAwsCredentialsCommand(options) {
28716
- const result = readToken(options.audience);
28713
+ async function identityAwsCredentialsCommand(options) {
28714
+ const result = await fetchToken(options.audience);
28717
28715
  if (!result.ok) {
28718
28716
  console.error(import_chalk5.default.red(result.error.message));
28719
28717
  process.exitCode = 1;
@@ -33109,10 +33107,10 @@ if (isAgentMode()) {
33109
33107
  });
33110
33108
  }
33111
33109
  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));
33110
+ const identity = program.command("identity").description("Obtain short-lived workspace identity tokens (OIDC) for the services this workspace calls");
33111
+ 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);
33112
+ 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);
33113
+ 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
33114
  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
33115
  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
33116
  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.716",
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": {