replicas-cli 0.3.107 → 0.3.108
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/index.cjs +146 -146
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7567,9 +7567,6 @@ var import_open = __toESM(require("open"));
|
|
|
7567
7567
|
function isRecord(value) {
|
|
7568
7568
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7569
7569
|
}
|
|
7570
|
-
function isNonEmptyString(value) {
|
|
7571
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
7572
|
-
}
|
|
7573
7570
|
|
|
7574
7571
|
// ../shared/src/json.ts
|
|
7575
7572
|
function safeJsonParse(str, fallback) {
|
|
@@ -24359,7 +24356,6 @@ var ENVIRONMENT_SETUP_LIMITS = {
|
|
|
24359
24356
|
mediaItems: 8,
|
|
24360
24357
|
repositories: 20
|
|
24361
24358
|
};
|
|
24362
|
-
var ENVIRONMENT_SETUP_VARIABLE_WAIT = { defaultMs: 5 * 6e4, maxMs: 15 * 6e4, pollMs: 5e3 };
|
|
24363
24359
|
var VARIABLE_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
24364
24360
|
var text2 = (max) => max === void 0 ? external_exports.string().trim() : external_exports.string().trim().max(max);
|
|
24365
24361
|
var requiredText = (max) => text2(max).min(1, "must not be empty");
|
|
@@ -24438,19 +24434,16 @@ function environmentSchema(limits) {
|
|
|
24438
24434
|
warm_hook: clearableText(limits.hookChars)
|
|
24439
24435
|
});
|
|
24440
24436
|
}
|
|
24441
|
-
function variableSchema(limits, requiredDefault) {
|
|
24442
|
-
return external_exports.object({
|
|
24443
|
-
key: external_exports.string().regex(VARIABLE_KEY_RE, "must be a valid environment variable name").max(limits.variableKeyChars ?? Number.POSITIVE_INFINITY),
|
|
24444
|
-
description: optionalText2(limits.variableDescriptionChars).transform((value) => value ?? ""),
|
|
24445
|
-
required: external_exports.boolean().default(requiredDefault)
|
|
24446
|
-
});
|
|
24447
|
-
}
|
|
24448
24437
|
function proposalSchema(limits) {
|
|
24449
24438
|
return external_exports.object({
|
|
24450
24439
|
summary: requiredText(limits.summaryChars),
|
|
24451
24440
|
stack: list(text2(), limits.stackItems).default([]).transform((items) => items.filter(Boolean)),
|
|
24452
24441
|
environment: environmentSchema(limits),
|
|
24453
|
-
variables: list(
|
|
24442
|
+
variables: list(external_exports.object({
|
|
24443
|
+
key: external_exports.string().regex(VARIABLE_KEY_RE, "must be a valid environment variable name").max(limits.variableKeyChars ?? Number.POSITIVE_INFINITY),
|
|
24444
|
+
description: optionalText2(limits.variableDescriptionChars).transform((value) => value ?? ""),
|
|
24445
|
+
required: external_exports.boolean().default(false)
|
|
24446
|
+
}), limits.variables).default([]),
|
|
24454
24447
|
automations: automationListSchema(limits),
|
|
24455
24448
|
verification: list(external_exports.object({
|
|
24456
24449
|
label: requiredText(limits.nameChars),
|
|
@@ -24465,11 +24458,6 @@ function proposalSchema(limits) {
|
|
|
24465
24458
|
}
|
|
24466
24459
|
var environmentSetupProposalSchema = proposalSchema({});
|
|
24467
24460
|
var environmentSetupProposalSubmissionSchema = proposalSchema(ENVIRONMENT_SETUP_LIMITS);
|
|
24468
|
-
var environmentSetupVariableRequestSchema = variableSchema({}, false).extend({
|
|
24469
|
-
status: external_exports.enum(["pending", "provided", "skipped"]),
|
|
24470
|
-
requested_at: external_exports.string(),
|
|
24471
|
-
resolved_at: external_exports.string().nullable().default(null)
|
|
24472
|
-
});
|
|
24473
24461
|
var environmentSetupRunStateSchema = external_exports.object({
|
|
24474
24462
|
status: external_exports.enum(ENVIRONMENT_SETUP_RUN_STATUSES),
|
|
24475
24463
|
steps: external_exports.partialRecord(external_exports.enum(ENVIRONMENT_SETUP_STEPS.map((step) => step.key)), external_exports.enum(ENVIRONMENT_SETUP_STEP_STATUSES)).default({}),
|
|
@@ -24477,25 +24465,13 @@ var environmentSetupRunStateSchema = external_exports.object({
|
|
|
24477
24465
|
applied: external_exports.object({
|
|
24478
24466
|
applied_at: external_exports.string(),
|
|
24479
24467
|
automation_ids: external_exports.array(external_exports.string())
|
|
24480
|
-
}).nullable().default(null)
|
|
24481
|
-
variable_requests: external_exports.array(environmentSetupVariableRequestSchema).default([])
|
|
24468
|
+
}).nullable().default(null)
|
|
24482
24469
|
});
|
|
24483
24470
|
var applyEnvironmentSetupRequestSchema = external_exports.object({
|
|
24484
24471
|
environment: environmentSchema(ENVIRONMENT_SETUP_LIMITS).extend({ name: requiredText(ENVIRONMENT_SETUP_LIMITS.nameChars) }),
|
|
24485
24472
|
variables: list(external_exports.object({ key: external_exports.string().regex(VARIABLE_KEY_RE), value: external_exports.string() }), ENVIRONMENT_SETUP_LIMITS.variables).default([]).refine((variables) => new Set(variables.map((variable) => variable.key)).size === variables.length, "must have unique keys").transform((variables) => variables.filter((variable) => variable.value.length > 0)),
|
|
24486
24473
|
automations: automationListSchema(ENVIRONMENT_SETUP_LIMITS)
|
|
24487
24474
|
});
|
|
24488
|
-
var requestEnvironmentSetupVariablesSchema = external_exports.object({
|
|
24489
|
-
variables: list(variableSchema(ENVIRONMENT_SETUP_LIMITS, true), ENVIRONMENT_SETUP_LIMITS.variables).min(1, "must not be empty")
|
|
24490
|
-
}).superRefine(({ variables }, ctx) => {
|
|
24491
|
-
if (new Set(variables.map((variable) => variable.key)).size !== variables.length) {
|
|
24492
|
-
ctx.addIssue({ code: "custom", path: ["variables"], message: "must have unique keys" });
|
|
24493
|
-
}
|
|
24494
|
-
});
|
|
24495
|
-
var respondEnvironmentSetupVariablesSchema = external_exports.object({
|
|
24496
|
-
values: external_exports.record(external_exports.string().regex(VARIABLE_KEY_RE), external_exports.string()).default({}),
|
|
24497
|
-
skipped: external_exports.array(external_exports.string().regex(VARIABLE_KEY_RE)).default([])
|
|
24498
|
-
});
|
|
24499
24475
|
|
|
24500
24476
|
// ../shared/src/default-skills/replicas-agent/abilities/environment-setup.ts
|
|
24501
24477
|
var SECTION3 = `### Environment setup (agent-led)
|
|
@@ -24536,11 +24512,9 @@ When this workspace was started as a setup run, the dashboard shows a live check
|
|
|
24536
24512
|
\`\`\`bash
|
|
24537
24513
|
replicas environment-setup status
|
|
24538
24514
|
replicas environment-setup step <key> <status>
|
|
24539
|
-
replicas environment-setup request-variables -f <path>
|
|
24540
|
-
replicas environment-setup wait-variables [-t 5m]
|
|
24541
24515
|
\`\`\`
|
|
24542
24516
|
|
|
24543
|
-
\`status\` prints the environment, repositories, current steps
|
|
24517
|
+
\`status\` prints the environment, repositories, and current steps. Step statuses are \`pending\`, \`in_progress\`, \`completed\`, and \`skipped\`. Step keys, in order: \`understand\`, \`install\`, \`verify\`, \`configure\`, \`propose\`. Mark the first step \`in_progress\` before your first tool call, keep exactly one step \`in_progress\`, and mark a step \`completed\` only after its work actually succeeded. Use \`skipped\` when something genuinely cannot run (for example no browser UI to record) and say so in the proposal.
|
|
24544
24518
|
|
|
24545
24519
|
If \`replicas environment-setup status\` reports that there is no active setup run, you are in an ordinary workspace, either because the user asked for this or because the fix you found while debugging belongs in the environment. Follow the same discovery and verification steps, show the exact change in chat, and offer to apply and test it yourself. Ask the user before changing the environment, since it affects every workspace built from it. Once they approve:
|
|
24546
24520
|
|
|
@@ -24566,34 +24540,18 @@ Use subagents for broad, independent analysis when available, but read the files
|
|
|
24566
24540
|
|
|
24567
24541
|
## Asking for variables
|
|
24568
24542
|
|
|
24569
|
-
The user may be watching
|
|
24570
|
-
|
|
24571
|
-
1. Right after step 1, request in one batch only the secrets you need to run the app and test its main flow (usually including sign-in) that you cannot replace locally. First check for a local stand-in the app supports, such as a local database, an auth emulator, or documented test credentials. Do not request values you can generate, values that duplicate another key, or values that are not secret, and list optional integrations in the proposal instead of requesting them. \`required\` means the app cannot start, or its main flow cannot be tested, without it.
|
|
24572
|
-
|
|
24573
|
-
\`\`\`bash
|
|
24574
|
-
replicas environment-setup request-variables --file /tmp/variables.json
|
|
24575
|
-
\`\`\`
|
|
24576
|
-
|
|
24577
|
-
\`\`\`json
|
|
24578
|
-
{
|
|
24579
|
-
"variables": [
|
|
24580
|
-
{ "key": "FIREBASE_PRIVATE_KEY", "description": "Service account private key for a dev Firebase project; sign-in cannot be tested without it", "required": true }
|
|
24581
|
-
]
|
|
24582
|
-
}
|
|
24583
|
-
\`\`\`
|
|
24543
|
+
The user may be watching or may have walked away, so ask early and wait late:
|
|
24584
24544
|
|
|
24585
|
-
|
|
24586
|
-
3. At the point you actually need a value, say in chat which keys you are waiting for and why, then block on it once:
|
|
24545
|
+
1. Right after step 1, ask in one command for only the secrets you need to run the app and test its main flow (usually including sign-in) that you cannot replace locally. First check for a local stand-in the app supports, such as a local database, an auth emulator, or documented test credentials. Do not request values you can generate, values that duplicate another key, or values that are not secret, and list optional integrations in the proposal instead of requesting them.
|
|
24587
24546
|
|
|
24588
24547
|
\`\`\`bash
|
|
24589
|
-
replicas environment
|
|
24590
|
-
replicas environment-setup wait-variables --keys FIREBASE_PRIVATE_KEY --timeout 15m
|
|
24548
|
+
replicas environment vars request FIREBASE_PRIVATE_KEY --reason "Service account key for a dev Firebase project, to test sign-in" --no-wait
|
|
24591
24549
|
\`\`\`
|
|
24592
24550
|
|
|
24593
|
-
|
|
24594
|
-
|
|
24595
|
-
|
|
24596
|
-
|
|
24551
|
+
2. Keep working on everything that does not need the values. The form appears above the chat composer; the user pastes values there (saved encrypted on the environment, never in chat) or skips them.
|
|
24552
|
+
3. At the point you actually need a value, say in chat which keys you are waiting for and why, then run the same command without \`--no-wait\`. It returns when the keys are provided or skipped, or when the time runs out (\`--timeout 15m\`), and loads provided values into \`~/.replicas/runtime-env.sh\`. Run commands as \`. ~/.replicas/runtime-env.sh && <command>\` and restart anything you started with \`replicas service start\` before that point. Never print the values.
|
|
24553
|
+
4. For anything skipped or still unanswered, use the safest local substitute (local database, mock, documented test value), say so in the verification rows, and list the key in the proposal, marked required only if the app cannot start or its main flow cannot be tested without it. Do not ask again for a key the user skipped.
|
|
24554
|
+
5. Values the user saved are already on the environment. Still list those keys in the proposal so the review shows them as set.
|
|
24597
24555
|
|
|
24598
24556
|
## 2. Install and run
|
|
24599
24557
|
|
|
@@ -24606,7 +24564,7 @@ Work from the smallest failing layer, in this workspace:
|
|
|
24606
24564
|
|
|
24607
24565
|
Use the repository's pinned package manager and lockfile. Do not upgrade dependencies, rewrite lockfiles, or change application code to make setup pass; environment configuration and narrowly necessary fixes to setup scripts are in scope. Do not deploy, publish, or touch production resources.
|
|
24608
24566
|
|
|
24609
|
-
When something needs a secret you do not have, request it and
|
|
24567
|
+
When something needs a secret you do not have, request it and wait for it at the point you need it (see "Asking for variables"). If it is skipped or unanswered, use the safest local substitute, keep going, and record the variable in the proposal with a clear description. Never invent or paste secret values anywhere.
|
|
24610
24568
|
|
|
24611
24569
|
## 3. Verify and record
|
|
24612
24570
|
|
|
@@ -24744,7 +24702,7 @@ Keep it under 250 words plus the lists. Do not paste the hooks or system prompt
|
|
|
24744
24702
|
|
|
24745
24703
|
## Safety
|
|
24746
24704
|
|
|
24747
|
-
- Never put tokens, passwords, or private keys in hooks, prompts, files, logs, or chat. Values that arrive through \`
|
|
24705
|
+
- Never put tokens, passwords, or private keys in hooks, prompts, files, logs, or chat. Values that arrive through \`replicas environment vars request\` are written to \`~/.replicas/runtime-env.sh\`; never echo, cat, or paste them.
|
|
24748
24706
|
- Do not weaken network, certificate, or package-integrity settings to make setup pass.
|
|
24749
24707
|
- Do not run destructive git operations, force pushes, or anything against production endpoints.
|
|
24750
24708
|
- Do not change application code to hide an environment problem. Report it instead.
|
|
@@ -25960,6 +25918,7 @@ In agent mode the CLI hides commands that don't make sense for in-workspace agen
|
|
|
25960
25918
|
| --- | --- |
|
|
25961
25919
|
| "What environments do I have?" | \`replicas environment list\` |
|
|
25962
25920
|
| "Add API key X to my staging env" | \`replicas environment vars set <env> <KEY> <VALUE>\` |
|
|
25921
|
+
| You need a secret or API key you don't have | \`replicas environment vars request <KEY>... --reason "..."\` (see "Asking the user for variables") |
|
|
25963
25922
|
| "Make me a \`.env\` file in the workspace with these vars" | \`replicas environment files set <env> .env --content "..."\` |
|
|
25964
25923
|
| "Create a new env for my \`acme/web\` repo" | \`replicas environment create <name> -r acme/web\` |
|
|
25965
25924
|
| "What automations do I have?" | \`replicas automation list\` |
|
|
@@ -26007,6 +25966,18 @@ replicas environment vars delete <env> <KEY|VARIABLE_ID> [--force]
|
|
|
26007
25966
|
|
|
26008
25967
|
These variables are injected into every workspace built from this environment as actual \`env\` vars \u2014 perfect for API keys, feature flags, etc. Anything you want available to the agent's tools (Claude, codex, your code, etc.) goes here.
|
|
26009
25968
|
|
|
25969
|
+
### Asking the user for variables
|
|
25970
|
+
|
|
25971
|
+
When you need a secret, API key, or other value you don't have, ask with one command. Never ask for values in chat, and never write instructions for adding them in the dashboard:
|
|
25972
|
+
|
|
25973
|
+
\`\`\`bash
|
|
25974
|
+
replicas environment vars request AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY --reason "Deploy the stack to AWS" [--optional AWS_REGION]
|
|
25975
|
+
\`\`\`
|
|
25976
|
+
|
|
25977
|
+
The user gets a form above the chat composer in Replicas, where they can paste a whole \`.env\` and choose to save the values to the environment or keep them in this workspace only. The command waits up to 10 minutes (\`--timeout\`), then writes the values to \`~/.replicas/runtime-env.sh\` and prints only the key names. Your current shell doesn't have them yet, so run commands as \`. ~/.replicas/runtime-env.sh && <command>\`, and restart anything you started earlier. Never print the values.
|
|
25978
|
+
|
|
25979
|
+
In workspaces started from Slack, Linear, or an automation, nobody may be watching Replicas, so the command returns right away with a link to the form; share it where you are talking with the user. If the values don't arrive in time, the request stays open. Either way you get a message when the user saves; run the same command again then. If a saved value is wrong, for example the service rejects it, add \`--replace\` to ask for a new one.
|
|
25980
|
+
|
|
26010
25981
|
### Environment files
|
|
26011
25982
|
|
|
26012
25983
|
Files materialize on the workspace filesystem when a workspace starts, at the destination path you specify. Use this for things like \`.env\` files at the repo root, dotfiles in \`~\`, prompt files for agents, etc.
|
|
@@ -26166,8 +26137,7 @@ Repos are connected via the GitHub integration in the dashboard \u2014 not from
|
|
|
26166
26137
|
var REPLICAS_ABILITY = {
|
|
26167
26138
|
label: "Replicas",
|
|
26168
26139
|
description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
|
|
26169
|
-
|
|
26170
|
-
bullet: "",
|
|
26140
|
+
bullet: "- Getting a secret, API key, or token you don't have from the user: run `replicas environment vars request <KEY>...`, which shows them a form. Never ask for the value in chat or tell them to add it in settings.",
|
|
26171
26141
|
section: SECTION11,
|
|
26172
26142
|
referenceFile: { name: "REPLICAS.md", content: REFERENCE11 },
|
|
26173
26143
|
locked: true
|
|
@@ -28760,7 +28730,7 @@ var headlessAgentRequestSchema = external_exports.object({
|
|
|
28760
28730
|
});
|
|
28761
28731
|
|
|
28762
28732
|
// ../shared/src/cli-version.ts
|
|
28763
|
-
var CLI_VERSION = "0.3.
|
|
28733
|
+
var CLI_VERSION = "0.3.108";
|
|
28764
28734
|
|
|
28765
28735
|
// ../shared/src/version.ts
|
|
28766
28736
|
function compareVersions(v1, v2) {
|
|
@@ -29301,6 +29271,11 @@ var workspaceChangedEventSchema = external_exports.discriminatedUnion("type", [
|
|
|
29301
29271
|
workspaceId: external_exports.string(),
|
|
29302
29272
|
ts: external_exports.string()
|
|
29303
29273
|
}),
|
|
29274
|
+
external_exports.object({
|
|
29275
|
+
type: external_exports.literal("variable-request.changed"),
|
|
29276
|
+
workspaceId: external_exports.string(),
|
|
29277
|
+
ts: external_exports.string()
|
|
29278
|
+
}),
|
|
29304
29279
|
external_exports.object({
|
|
29305
29280
|
type: external_exports.literal("pull-request.changed"),
|
|
29306
29281
|
prUrl: external_exports.string(),
|
|
@@ -29515,6 +29490,29 @@ async function readSseStream(body, onEvent, shouldContinue = () => true) {
|
|
|
29515
29490
|
}
|
|
29516
29491
|
}
|
|
29517
29492
|
|
|
29493
|
+
// ../shared/src/routes/variable-requests.ts
|
|
29494
|
+
var VARIABLE_REQUEST_WAIT = { defaultMs: 10 * 6e4, maxMs: 30 * 6e4, pollMs: 3e3, waiterStaleMs: 15e3 };
|
|
29495
|
+
var VARIABLE_REQUEST_DESTINATIONS = ["environment", "workspace"];
|
|
29496
|
+
var requestedVariableSchema = external_exports.object({
|
|
29497
|
+
key: external_exports.string().regex(VARIABLE_KEY_RE, "must be a valid environment variable name").max(ENVIRONMENT_SETUP_LIMITS.variableKeyChars),
|
|
29498
|
+
required: external_exports.boolean(),
|
|
29499
|
+
replace: external_exports.boolean().optional()
|
|
29500
|
+
});
|
|
29501
|
+
var requestedVariablesSchema = external_exports.array(requestedVariableSchema).min(1, "must not be empty").max(ENVIRONMENT_SETUP_LIMITS.variables).refine((variables) => new Set(variables.map((variable) => variable.key)).size === variables.length, "must have unique keys");
|
|
29502
|
+
var createVariableRequestSchema = external_exports.object({
|
|
29503
|
+
variables: requestedVariablesSchema,
|
|
29504
|
+
reason: external_exports.string().trim().max(500).nullish().transform((value) => value || null),
|
|
29505
|
+
wait: external_exports.boolean().default(true)
|
|
29506
|
+
});
|
|
29507
|
+
var respondVariableRequestSchema = external_exports.discriminatedUnion("action", [
|
|
29508
|
+
external_exports.object({
|
|
29509
|
+
action: external_exports.literal("provide"),
|
|
29510
|
+
destination: external_exports.enum(VARIABLE_REQUEST_DESTINATIONS),
|
|
29511
|
+
values: external_exports.record(external_exports.string().regex(VARIABLE_KEY_RE), external_exports.string())
|
|
29512
|
+
}),
|
|
29513
|
+
external_exports.object({ action: external_exports.literal("skip") })
|
|
29514
|
+
]);
|
|
29515
|
+
|
|
29518
29516
|
// src/lib/auth.ts
|
|
29519
29517
|
var import_supabase_js2 = require("@supabase/supabase-js");
|
|
29520
29518
|
|
|
@@ -31519,6 +31517,8 @@ var import_chalk21 = __toESM(require("chalk"));
|
|
|
31519
31517
|
|
|
31520
31518
|
// src/commands/environment.ts
|
|
31521
31519
|
var import_fs4 = __toESM(require("fs"));
|
|
31520
|
+
var import_os3 = __toESM(require("os"));
|
|
31521
|
+
var import_path5 = __toESM(require("path"));
|
|
31522
31522
|
var import_chalk20 = __toESM(require("chalk"));
|
|
31523
31523
|
|
|
31524
31524
|
// src/commands/repositories.ts
|
|
@@ -31813,6 +31813,88 @@ async function envVarsDeleteCommand(envIdOrName, keyOrId, options) {
|
|
|
31813
31813
|
Deleted variable ${keyOrId}.
|
|
31814
31814
|
`);
|
|
31815
31815
|
}
|
|
31816
|
+
var RUNTIME_ENV_FILE = import_path5.default.join(import_os3.default.homedir(), ".replicas", "runtime-env.sh");
|
|
31817
|
+
async function envVarsRequestCommand(requiredKeys, options) {
|
|
31818
|
+
ensureOrgApiAuthenticated();
|
|
31819
|
+
const optional2 = new Set(options.optional.flatMap((value) => value.split(",")).map((key) => key.trim()).filter(Boolean));
|
|
31820
|
+
const keys = [.../* @__PURE__ */ new Set([...requiredKeys, ...optional2])];
|
|
31821
|
+
if (keys.length === 0) throw new Error("Name at least one variable to request");
|
|
31822
|
+
const invalid = keys.find((key) => !VARIABLE_KEY_RE.test(key));
|
|
31823
|
+
if (invalid) throw new Error(`${invalid} is not a valid environment variable name`);
|
|
31824
|
+
const timeout = options.timeout ? /^(\d+)(s|m)?$/.exec(options.timeout.trim()) : null;
|
|
31825
|
+
if (options.timeout && !timeout) throw new Error(`Invalid timeout "${options.timeout}". Use seconds or minutes, for example 90s or 10m`);
|
|
31826
|
+
const timeoutMs = timeout ? Math.min(Math.max(Number(timeout[1]) * (timeout[2] === "m" ? 6e4 : 1e3), VARIABLE_REQUEST_WAIT.pollMs), VARIABLE_REQUEST_WAIT.maxMs) : VARIABLE_REQUEST_WAIT.defaultMs;
|
|
31827
|
+
const readRuntimeEnv = () => import_fs4.default.existsSync(RUNTIME_ENV_FILE) ? import_fs4.default.readFileSync(RUNTIME_ENV_FILE, "utf8") : "";
|
|
31828
|
+
const local = parsePosixEnvFile(readRuntimeEnv());
|
|
31829
|
+
const available = options.replace ? [] : keys.filter((key) => Object.hasOwn(local, key) && local[key].length > 0);
|
|
31830
|
+
const wanted = keys.filter((key) => !available.includes(key));
|
|
31831
|
+
const loadHint = import_chalk20.default.gray(`
|
|
31832
|
+
Values are never printed. Load them into a command with: . ~/.replicas/runtime-env.sh && <command>
|
|
31833
|
+
`);
|
|
31834
|
+
if (wanted.length === 0) {
|
|
31835
|
+
output({ status: "provided", exported: available, missing: [] }, () => {
|
|
31836
|
+
console.log(`
|
|
31837
|
+
${import_chalk20.default.green("Already available:")} ${available.join(", ")}`);
|
|
31838
|
+
console.log(loadHint);
|
|
31839
|
+
console.log(import_chalk20.default.gray(" If a value is wrong, run this again with --replace to ask the user for a new one.\n"));
|
|
31840
|
+
});
|
|
31841
|
+
return;
|
|
31842
|
+
}
|
|
31843
|
+
const created = await orgAuthenticatedFetch("/v1/variable-requests", {
|
|
31844
|
+
method: "POST",
|
|
31845
|
+
body: { reason: options.reason, variables: wanted.map((key) => ({ key, required: !optional2.has(key), ...options.replace ? { replace: true } : {} })), wait: options.wait !== false }
|
|
31846
|
+
});
|
|
31847
|
+
let { request } = created;
|
|
31848
|
+
if (request.status === "pending" && created.wait) {
|
|
31849
|
+
if (!isJsonMode()) console.log(import_chalk20.default.gray(`
|
|
31850
|
+
Asked the user for ${wanted.join(", ")} above the chat composer. Waiting up to ${timeoutMs >= 6e4 ? `${Math.round(timeoutMs / 6e4)} minute(s)` : `${timeoutMs / 1e3} seconds`}\u2026`));
|
|
31851
|
+
const deadline = Date.now() + timeoutMs;
|
|
31852
|
+
while (request.status === "pending" && Date.now() < deadline) {
|
|
31853
|
+
await new Promise((resolve3) => setTimeout(resolve3, VARIABLE_REQUEST_WAIT.pollMs));
|
|
31854
|
+
({ request } = await orgAuthenticatedFetch(`/v1/variable-requests/${request.id}`));
|
|
31855
|
+
}
|
|
31856
|
+
if (request.status === "pending") ({ request } = await orgAuthenticatedFetch(`/v1/variable-requests/${request.id}?waiting=false`));
|
|
31857
|
+
}
|
|
31858
|
+
let exported = [];
|
|
31859
|
+
if (request.status === "provided") {
|
|
31860
|
+
const loaded = await orgAuthenticatedFetch(`/v1/variable-requests/${request.id}/load`, { method: "POST" });
|
|
31861
|
+
const variables = loaded.variables.filter((variable) => !variable.key.startsWith("REPLICAS_"));
|
|
31862
|
+
const existing = readRuntimeEnv();
|
|
31863
|
+
const env = { ...parsePosixEnvFile(existing), ...Object.fromEntries(variables.map((variable) => [variable.key, variable.value])) };
|
|
31864
|
+
import_fs4.default.mkdirSync(import_path5.default.dirname(RUNTIME_ENV_FILE), { recursive: true });
|
|
31865
|
+
import_fs4.default.writeFileSync(RUNTIME_ENV_FILE, serializePosixEnvFile(env, parsePosixEnvUnsetKeys(existing).filter((key) => !env[key])), { mode: 384 });
|
|
31866
|
+
import_fs4.default.chmodSync(RUNTIME_ENV_FILE, 384);
|
|
31867
|
+
exported = [...variables.map((variable) => variable.key), ...loaded.already_loaded];
|
|
31868
|
+
}
|
|
31869
|
+
if (request.status !== "pending") await orgAuthenticatedFetch(`/v1/variable-requests/${request.id}/ack`, { method: "POST" });
|
|
31870
|
+
const missing = wanted.filter((key) => !exported.includes(key));
|
|
31871
|
+
output({ status: request.status, request_id: request.id, destination: request.destination, url: created.url, exported: [...available, ...exported], missing }, () => {
|
|
31872
|
+
console.log("");
|
|
31873
|
+
if (exported.length > 0 || available.length > 0) {
|
|
31874
|
+
const where = request.destination === "workspace" ? "this workspace only" : "saved to the environment";
|
|
31875
|
+
console.log(` ${import_chalk20.default.green("Provided:")} ${[...available, ...exported].join(", ")} ${import_chalk20.default.gray(`(${where})`)}`);
|
|
31876
|
+
}
|
|
31877
|
+
if (request.status === "pending") {
|
|
31878
|
+
console.log(` ${import_chalk20.default.yellow("Still waiting for:")} ${missing.join(", ")}`);
|
|
31879
|
+
console.log(import_chalk20.default.gray(options.wait === false ? `
|
|
31880
|
+
Asked above the chat composer (${created.url}). Keep working, and run this command again when you need the values.
|
|
31881
|
+
` : created.wait ? `
|
|
31882
|
+
The form stays open above the chat composer (${created.url}). End your turn and say what the values are for. When the user saves, you get a message; run this command again then.
|
|
31883
|
+
` : `
|
|
31884
|
+
The user may not be watching Replicas. Send them this link where you are talking with them (the Slack thread, Linear issue, or pull request) and ask them to add the values there, never in chat: ${created.url}
|
|
31885
|
+
When they save, you get a message; run this command again then.
|
|
31886
|
+
`));
|
|
31887
|
+
return;
|
|
31888
|
+
}
|
|
31889
|
+
if (missing.length > 0) console.log(` ${import_chalk20.default.gray(request.status === "skipped" ? "Skipped by the user:" : "Not provided:")} ${missing.join(", ")}`);
|
|
31890
|
+
if (exported.length === 0) {
|
|
31891
|
+
console.log(import_chalk20.default.gray("\n Continue without them, or ask the user how they want to proceed.\n"));
|
|
31892
|
+
return;
|
|
31893
|
+
}
|
|
31894
|
+
console.log(loadHint);
|
|
31895
|
+
if (options.replace) console.log(import_chalk20.default.gray(" Leave out --replace from now on; running it again asks the user again.\n"));
|
|
31896
|
+
});
|
|
31897
|
+
}
|
|
31816
31898
|
function printFile(f) {
|
|
31817
31899
|
console.log(import_chalk20.default.white(` ${f.path}`));
|
|
31818
31900
|
console.log(import_chalk20.default.gray(` ID: ${f.id}`));
|
|
@@ -32819,8 +32901,6 @@ async function pluginsDisconnectCommand(pluginId, options) {
|
|
|
32819
32901
|
|
|
32820
32902
|
// src/commands/environment-setup.ts
|
|
32821
32903
|
var import_fs6 = __toESM(require("fs"));
|
|
32822
|
-
var import_os3 = __toESM(require("os"));
|
|
32823
|
-
var import_path5 = __toESM(require("path"));
|
|
32824
32904
|
var import_chalk25 = __toESM(require("chalk"));
|
|
32825
32905
|
var STEP_KEYS = ENVIRONMENT_SETUP_STEPS.map((step) => step.key).join(", ");
|
|
32826
32906
|
async function fetchCurrentRun() {
|
|
@@ -32845,10 +32925,6 @@ async function environmentSetupStatusCommand() {
|
|
|
32845
32925
|
const marker = status === "completed" ? import_chalk25.default.green("\u2713") : status === "in_progress" ? import_chalk25.default.cyan("\u25B8") : status === "skipped" ? import_chalk25.default.gray("\u2013") : import_chalk25.default.gray("\u25CB");
|
|
32846
32926
|
console.log(` ${marker} ${step.key.padEnd(11)} ${import_chalk25.default.gray(step.label)} ${import_chalk25.default.gray(`[${status}]`)}`);
|
|
32847
32927
|
}
|
|
32848
|
-
if (run2.variable_requests.length > 0) {
|
|
32849
|
-
console.log("\n Variables:");
|
|
32850
|
-
printVariableRequests(run2.variable_requests);
|
|
32851
|
-
}
|
|
32852
32928
|
console.log(run2.proposal ? import_chalk25.default.gray("\n A proposal has been submitted. Run `propose` again to replace it.\n") : import_chalk25.default.gray("\n No proposal yet. Submit one with `replicas environment-setup propose --file <path>`.\n"));
|
|
32853
32929
|
});
|
|
32854
32930
|
}
|
|
@@ -32894,83 +32970,6 @@ async function environmentSetupProposeCommand(options) {
|
|
|
32894
32970
|
console.log(import_chalk25.default.gray("\n The user reviews and saves it from the Environment panel; nothing has been applied yet.\n"));
|
|
32895
32971
|
});
|
|
32896
32972
|
}
|
|
32897
|
-
var RUNTIME_ENV_FILE = import_path5.default.join(import_os3.default.homedir(), ".replicas", "runtime-env.sh");
|
|
32898
|
-
var EXPORTED_KEYS_FILE = import_path5.default.join(import_os3.default.homedir(), ".replicas", "environment-setup-exported-keys.json");
|
|
32899
|
-
function printVariableRequests(requests) {
|
|
32900
|
-
for (const request of requests) {
|
|
32901
|
-
const marker = request.status === "provided" ? import_chalk25.default.green("\u2713") : request.status === "skipped" ? import_chalk25.default.gray("\u2013") : import_chalk25.default.cyan("\u2026");
|
|
32902
|
-
console.log(` ${marker} ${request.key.padEnd(32)} ${import_chalk25.default.gray(`[${request.status}${request.required ? ", required" : ""}]`)}`);
|
|
32903
|
-
}
|
|
32904
|
-
}
|
|
32905
|
-
async function environmentSetupRequestVariablesCommand(options) {
|
|
32906
|
-
const request = readJsonFile(options.file, "variable request");
|
|
32907
|
-
const { run: run2 } = await fetchCurrentRun();
|
|
32908
|
-
const result = await orgAuthenticatedFetch(`/v1/environment-setups/${run2.id}/variable-requests`, {
|
|
32909
|
-
method: "POST",
|
|
32910
|
-
body: request
|
|
32911
|
-
});
|
|
32912
|
-
output(result, ({ run: { variable_requests } }) => {
|
|
32913
|
-
console.log(import_chalk25.default.green("\n\u2713 Variables requested.\n"));
|
|
32914
|
-
printVariableRequests(variable_requests);
|
|
32915
|
-
console.log(import_chalk25.default.gray("\n The user sees this in the Environment panel. Keep working and run `replicas environment-setup wait-variables` when you need a value.\n"));
|
|
32916
|
-
});
|
|
32917
|
-
}
|
|
32918
|
-
function parseWaitTimeoutMs(raw) {
|
|
32919
|
-
if (!raw) return ENVIRONMENT_SETUP_VARIABLE_WAIT.defaultMs;
|
|
32920
|
-
const match = /^(\d+)(s|m)?$/.exec(raw.trim());
|
|
32921
|
-
if (!match) throw new Error(`Invalid timeout "${raw}". Use seconds or minutes, for example 90s or 5m`);
|
|
32922
|
-
const ms = Number(match[1]) * (match[2] === "m" ? 6e4 : 1e3);
|
|
32923
|
-
return Math.min(Math.max(ms, ENVIRONMENT_SETUP_VARIABLE_WAIT.pollMs), ENVIRONMENT_SETUP_VARIABLE_WAIT.maxMs);
|
|
32924
|
-
}
|
|
32925
|
-
function exportToRuntimeEnv(variables) {
|
|
32926
|
-
import_fs6.default.mkdirSync(import_path5.default.dirname(RUNTIME_ENV_FILE), { recursive: true });
|
|
32927
|
-
const existing = import_fs6.default.existsSync(RUNTIME_ENV_FILE) ? import_fs6.default.readFileSync(RUNTIME_ENV_FILE, "utf8") : "";
|
|
32928
|
-
const exportedKeys = import_fs6.default.existsSync(EXPORTED_KEYS_FILE) ? JSON.parse(import_fs6.default.readFileSync(EXPORTED_KEYS_FILE, "utf8")) : [];
|
|
32929
|
-
const owned = new Set(Array.isArray(exportedKeys) ? exportedKeys.filter(isNonEmptyString) : []);
|
|
32930
|
-
const env = parsePosixEnvFile(existing);
|
|
32931
|
-
for (const key of owned) delete env[key];
|
|
32932
|
-
const exported = variables.filter((variable) => !variable.key.startsWith("REPLICAS_"));
|
|
32933
|
-
for (const variable of exported) env[variable.key] = variable.value;
|
|
32934
|
-
const unsetKeys = new Set([...parsePosixEnvUnsetKeys(existing), ...owned].filter((key) => !env[key]));
|
|
32935
|
-
const content = serializePosixEnvFile(env, unsetKeys);
|
|
32936
|
-
if (content !== existing) {
|
|
32937
|
-
import_fs6.default.writeFileSync(RUNTIME_ENV_FILE, content, { mode: 384 });
|
|
32938
|
-
import_fs6.default.chmodSync(RUNTIME_ENV_FILE, 384);
|
|
32939
|
-
}
|
|
32940
|
-
import_fs6.default.writeFileSync(EXPORTED_KEYS_FILE, JSON.stringify(exported.map((variable) => variable.key)), { mode: 384 });
|
|
32941
|
-
return exported.map((variable) => variable.key);
|
|
32942
|
-
}
|
|
32943
|
-
var formatDuration = (ms) => ms >= 6e4 ? `${Math.round(ms / 6e4)} minute(s)` : `${Math.round(ms / 1e3)} seconds`;
|
|
32944
|
-
async function environmentSetupWaitVariablesCommand(options) {
|
|
32945
|
-
const timeoutMs = parseWaitTimeoutMs(options.timeout);
|
|
32946
|
-
const wanted = options.keys ? new Set(options.keys.split(",").map((key) => key.trim()).filter(Boolean)) : null;
|
|
32947
|
-
const blocking = (requests) => requests.filter((request) => request.status === "pending" && (wanted ? wanted.has(request.key) : request.required));
|
|
32948
|
-
let { run: run2 } = await fetchCurrentRun();
|
|
32949
|
-
const before = run2.variable_requests;
|
|
32950
|
-
if (blocking(before).length > 0) {
|
|
32951
|
-
console.log(import_chalk25.default.gray(`
|
|
32952
|
-
Waiting up to ${formatDuration(timeoutMs)} for: ${blocking(before).map((request) => request.key).join(", ")}`));
|
|
32953
|
-
const deadline = Date.now() + timeoutMs;
|
|
32954
|
-
while (blocking(run2.variable_requests).length > 0 && Date.now() < deadline) {
|
|
32955
|
-
await new Promise((resolve3) => setTimeout(resolve3, ENVIRONMENT_SETUP_VARIABLE_WAIT.pollMs));
|
|
32956
|
-
run2 = (await fetchCurrentRun()).run;
|
|
32957
|
-
}
|
|
32958
|
-
}
|
|
32959
|
-
const { variables } = await orgAuthenticatedFetch("/v1/environment-setups/current/variables");
|
|
32960
|
-
const exported = exportToRuntimeEnv(variables);
|
|
32961
|
-
const reserved = variables.map((variable) => variable.key).filter((key) => !exported.includes(key));
|
|
32962
|
-
const skipped = run2.variable_requests.filter((request) => request.status === "skipped").map((request) => request.key);
|
|
32963
|
-
const timedOut = blocking(run2.variable_requests).map((request) => request.key);
|
|
32964
|
-
output({ exported, reserved, skipped, timed_out: timedOut }, () => {
|
|
32965
|
-
console.log("");
|
|
32966
|
-
if (exported.length > 0) console.log(` ${import_chalk25.default.green("Available in new shells:")} ${exported.join(", ")}`);
|
|
32967
|
-
if (reserved.length > 0) console.log(` ${import_chalk25.default.yellow("Reserved by Replicas:")} ${reserved.join(", ")} (reserved names, not exported; rename these in the proposal)`);
|
|
32968
|
-
if (skipped.length > 0) console.log(` ${import_chalk25.default.gray("Skipped by the user:")} ${skipped.join(", ")}`);
|
|
32969
|
-
if (timedOut.length > 0) console.log(` ${import_chalk25.default.yellow("Not answered in time:")} ${timedOut.join(", ")}`);
|
|
32970
|
-
if (variables.length === 0 && skipped.length === 0 && timedOut.length === 0) console.log(import_chalk25.default.gray(" No variables were requested."));
|
|
32971
|
-
console.log(import_chalk25.default.gray("\n Values are never printed. Restart services you started before this command so they pick the variables up. Use a local substitute for anything skipped or unanswered and list it as required in the proposal.\n"));
|
|
32972
|
-
});
|
|
32973
|
-
}
|
|
32974
32973
|
|
|
32975
32974
|
// src/commands/automation.ts
|
|
32976
32975
|
var import_chalk26 = __toESM(require("chalk"));
|
|
@@ -34913,6 +34912,9 @@ var envVars = environment.command("vars").description("Manage environment variab
|
|
|
34913
34912
|
envVars.command("list <env>").description("List variables in an environment (values masked by default)").option("--reveal", "Show full variable values instead of masking them").action(run(envVarsListCommand));
|
|
34914
34913
|
envVars.command("set <env> <key> <value>").description("Create or update a variable").action(run(envVarsSetCommand));
|
|
34915
34914
|
envVars.command("delete <env> <key-or-id>").description("Delete a variable by key or ID").option("-f, --force", "Skip confirmation prompt").action(run(envVarsDeleteCommand));
|
|
34915
|
+
if (isAgentMode()) {
|
|
34916
|
+
envVars.command("request [keys...]").description("Ask the user for variables and load them into this workspace").option("-r, --reason <text>", "One sentence on what the values are for, shown to the user").option("--optional <keys>", "Comma-separated keys to ask for that the user may leave empty (repeatable)", (value, previous) => [...previous, value], []).option("--replace", "Ask for new values even if the keys are already set, for example when a saved key is rejected").option("-t, --timeout <duration>", "How long to wait for the user, for example 90s or 10m (default 10m)").option("--no-wait", "Return right after asking instead of waiting for the user").action(run(envVarsRequestCommand));
|
|
34917
|
+
}
|
|
34916
34918
|
var envFiles = environment.command("files").description("Manage environment files (mounted into workspaces at workspace start)");
|
|
34917
34919
|
envFiles.command("list <env>").description("List files in an environment").action(run(envFilesListCommand));
|
|
34918
34920
|
envFiles.command("set <env> <destination-path>").description("Create or update a file at <destination-path> inside the workspace").option("-c, --content <content>", "Inline file content").option("-f, --file <local-path>", "Read content from a local file").option("-n, --name <name>", "Display name (defaults to basename of destination path)").action(run(envFilesSetCommand));
|
|
@@ -34969,8 +34971,6 @@ if (isEnvironmentSetupWorkspace()) {
|
|
|
34969
34971
|
environmentSetup.command("status").description("Show the setup run attached to this workspace").action(run(environmentSetupStatusCommand));
|
|
34970
34972
|
environmentSetup.command("step <key> <status>").description("Update a checklist step (understand | install | verify | configure | propose) to pending | in_progress | completed | skipped").action(run(environmentSetupStepCommand));
|
|
34971
34973
|
environmentSetup.command("propose").description("Submit the proposed configuration for the user to review and save").requiredOption("-f, --file <path>", "Path to the proposal JSON file").action(run(environmentSetupProposeCommand));
|
|
34972
|
-
environmentSetup.command("request-variables").description("Ask the user for variables the app needs; returns immediately so you can keep working").requiredOption("-f, --file <path>", 'Path to a JSON file: { "variables": [{ "key", "description", "required" }] }').action(run(environmentSetupRequestVariablesCommand));
|
|
34973
|
-
environmentSetup.command("wait-variables").description("Wait until requested required variables are provided or skipped, then export them for new shells (default 5m)").option("-t, --timeout <duration>", "How long to wait, e.g. 90s or 5m (max 15m)").option("-k, --keys <keys>", "Comma-separated keys to wait for instead of every pending required key").action(run(environmentSetupWaitVariablesCommand));
|
|
34974
34974
|
}
|
|
34975
34975
|
if (isAgentMode()) {
|
|
34976
34976
|
const identity = program.command("identity").description("Obtain short-lived workspace identity tokens (OIDC) for the services this workspace calls");
|