projecta-rrr 1.24.5 → 1.24.6
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/AGENTS.md +1 -1
- package/CHANGELOG.md +11 -0
- package/package.json +1 -1
- package/rrr/lib/team-mode/manager.js +136 -5
- package/scripts/rrr-team-mode.js +10 -1
package/AGENTS.md
CHANGED
|
@@ -96,4 +96,4 @@ The RRR loop follows five steps:
|
|
|
96
96
|
- Use `$rrr-help` for full skill catalogue
|
|
97
97
|
- Trigger phrases are case-sensitive: `$rrr-plan-phase` not `$rrr-PlanPhase`
|
|
98
98
|
|
|
99
|
-
<!-- generated: 2026-05-
|
|
99
|
+
<!-- generated: 2026-05-13T03:49:42.852Z | source: commands/rrr/*.md | count: 59 skills -->
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable changes to RRR will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [1.24.6] - 2026-05-13
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Infisical machine-identity auth mode** — `$rrr-provision-coordinator --auth-mode infisical-machine-identity --infisical-project-id <id> --infisical-env <env>` writes a coordinator contract that fetches secrets at boot from Infisical via Universal Auth. Required runtime secrets become `RRR_INFI_CID` + `RRR_INFI_CS` only; Anthropic and GitHub tokens live in Infisical and rotate centrally.
|
|
11
|
+
- **Infisical Sprite bootstrap** — `$rrr-provision-sprites --apply --infisical-project-id <id>` installs Infisical CLI inside each Sprite, writes machine-identity creds, and wires `~/.profile` to fetch `ANTHROPIC_API_KEY` on every login shell. Uses `RRR_INFI_CID` + `RRR_INFI_CS` from the provisioning host's environment.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **Default coordinator auth mode** — `infisical-machine-identity` is now the default (was `claude-code-subscription`). The subscription path remains available via `--auth-mode claude-code-subscription`.
|
|
15
|
+
- **Coordinator README** — generated README emits the Infisical install + login + secret-fetch boot snippet when in Infisical mode; falls back to credentials-file injection or AI Gateway when in their respective modes.
|
|
16
|
+
- **`$rrr-coordinator-status`** — checks for `RRR_INFI_CID`/`RRR_INFI_CS` env and `infisical --version` when in Infisical mode.
|
|
17
|
+
|
|
7
18
|
## [1.24.5] - 2026-05-13
|
|
8
19
|
|
|
9
20
|
### Added
|
package/package.json
CHANGED
|
@@ -201,6 +201,9 @@ function buildSpriteProvisionPlan(projectRoot, options) {
|
|
|
201
201
|
const workdir = spriteWorkdir(config, runtime);
|
|
202
202
|
const selectedTeams = normalizeList(opts.teams || opts.team);
|
|
203
203
|
const teams = selectedTeams.length > 0 ? selectedTeams : Object.keys(config.teams || {});
|
|
204
|
+
const infisicalProjectId = opts.infisicalProjectId || null;
|
|
205
|
+
const infisicalEnv = opts.infisicalEnv || 'dev';
|
|
206
|
+
const infisicalEnabled = !!infisicalProjectId;
|
|
204
207
|
const rows = [];
|
|
205
208
|
|
|
206
209
|
for (const rawTeam of teams) {
|
|
@@ -216,6 +219,34 @@ function buildSpriteProvisionPlan(projectRoot, options) {
|
|
|
216
219
|
: `git clone <repo-url> ${workdir}`;
|
|
217
220
|
const checkoutCommand = `gh auth setup-git >/dev/null 2>&1 || true; git fetch origin && git checkout -B ${teamConfig.branch} origin/${config.github.base_branch}`;
|
|
218
221
|
|
|
222
|
+
const loaderScript = [
|
|
223
|
+
'[ -f ~/.infisical-machine-creds ] && source ~/.infisical-machine-creds',
|
|
224
|
+
'if [ -n "$RRR_INFI_CID" ] && [ -n "$RRR_INFI_CS" ]; then',
|
|
225
|
+
' _IT=$(infisical login --method=universal-auth --client-id="$RRR_INFI_CID" --client-secret="$RRR_INFI_CS" --plain --silent 2>/dev/null)',
|
|
226
|
+
' if [ -n "$_IT" ]; then',
|
|
227
|
+
' export ANTHROPIC_API_KEY=$(infisical secrets get ANTHROPIC_API_KEY --token="$_IT" --projectId="$RRR_INFI_PROJ" --env="${RRR_INFI_ENV:-dev}" --plain --silent 2>/dev/null)',
|
|
228
|
+
' unset _IT',
|
|
229
|
+
' fi',
|
|
230
|
+
'fi'
|
|
231
|
+
].join('\n');
|
|
232
|
+
const credsScript = [
|
|
233
|
+
'umask 077 && cat > ~/.infisical-machine-creds <<EOF',
|
|
234
|
+
'export RRR_INFI_CID="$RRR_INFI_CID"',
|
|
235
|
+
'export RRR_INFI_CS="$RRR_INFI_CS"',
|
|
236
|
+
'export RRR_INFI_PROJ="$RRR_INFI_PROJ"',
|
|
237
|
+
'export RRR_INFI_ENV="$RRR_INFI_ENV"',
|
|
238
|
+
'EOF'
|
|
239
|
+
].join('\n');
|
|
240
|
+
const writeLoaderCommand = `cat > ~/.infisical-load.sh <<'LOADER'\n${loaderScript}\nLOADER\ngrep -q "infisical-load.sh" ~/.profile || echo "[ -f ~/.infisical-load.sh ] && source ~/.infisical-load.sh" >> ~/.profile`;
|
|
241
|
+
const infisicalCommands = infisicalEnabled ? [
|
|
242
|
+
'# Install Infisical CLI for runtime secret fetch',
|
|
243
|
+
`sprite exec -s ${teamConfig.sprite} -- bash -lc 'command -v infisical || (curl -1sLf "https://artifacts-cli.infisical.com/setup.deb.sh" | sudo -E bash && sudo apt-get install -y infisical)'`,
|
|
244
|
+
'# Write machine-identity credentials (read-only, mode 600)',
|
|
245
|
+
`sprite exec -s ${teamConfig.sprite} --env "RRR_INFI_CID=$RRR_INFI_CID,RRR_INFI_CS=$RRR_INFI_CS,RRR_INFI_PROJ=${infisicalProjectId},RRR_INFI_ENV=${infisicalEnv}" -- bash -lc ${JSON.stringify(credsScript)}`,
|
|
246
|
+
'# Loader fetches ANTHROPIC_API_KEY on every login shell',
|
|
247
|
+
`sprite exec -s ${teamConfig.sprite} -- bash -lc ${JSON.stringify(writeLoaderCommand)}`
|
|
248
|
+
] : [];
|
|
249
|
+
|
|
219
250
|
rows.push({
|
|
220
251
|
team,
|
|
221
252
|
github_team: teamConfig.github_team,
|
|
@@ -226,6 +257,7 @@ function buildSpriteProvisionPlan(projectRoot, options) {
|
|
|
226
257
|
repo,
|
|
227
258
|
setup,
|
|
228
259
|
checks,
|
|
260
|
+
infisical: infisicalEnabled ? { project_id: infisicalProjectId, env: infisicalEnv } : null,
|
|
229
261
|
commands: [
|
|
230
262
|
`sprite create ${teamConfig.sprite} --skip-console`,
|
|
231
263
|
`sprite exec -s ${teamConfig.sprite} -- mkdir -p /home/sprite/work`,
|
|
@@ -234,12 +266,13 @@ function buildSpriteProvisionPlan(projectRoot, options) {
|
|
|
234
266
|
`sprite exec -s ${teamConfig.sprite} --dir ${workdir} --env GITHUB_TOKEN=$GITHUB_TOKEN -- bash -lc '${checkoutCommand}'`,
|
|
235
267
|
`sprite exec -s ${teamConfig.sprite} --dir ${workdir} -- npx projecta-rrr@latest --global --yes`,
|
|
236
268
|
...setup.map(cmd => `sprite exec -s ${teamConfig.sprite} --dir ${workdir} -- bash -lc '${cmd}'`),
|
|
269
|
+
...infisicalCommands,
|
|
237
270
|
`sprite checkpoint create -s ${teamConfig.sprite} --comment "rrr clean-ready ${config.milestone} ${team}"`
|
|
238
271
|
]
|
|
239
272
|
});
|
|
240
273
|
}
|
|
241
274
|
|
|
242
|
-
return { config, runtime, workdir, rows };
|
|
275
|
+
return { config, runtime, workdir, rows, infisical: infisicalEnabled ? { project_id: infisicalProjectId, env: infisicalEnv } : null };
|
|
243
276
|
}
|
|
244
277
|
|
|
245
278
|
function renderSpriteProvisionPlan(plan) {
|
|
@@ -416,6 +449,31 @@ function applySpriteProvisionPlan(projectRoot, options) {
|
|
|
416
449
|
const setup = execCapture('sprite', ['exec', '-s', row.sprite, '--dir', row.workdir, '--', 'bash', '-lc', command]);
|
|
417
450
|
teamResults.push({ step: `setup: ${command}`, ...setup });
|
|
418
451
|
}
|
|
452
|
+
if (row.infisical && process.env.RRR_INFI_CID && process.env.RRR_INFI_CS) {
|
|
453
|
+
const cid = process.env.RRR_INFI_CID;
|
|
454
|
+
const cs = process.env.RRR_INFI_CS;
|
|
455
|
+
const proj = row.infisical.project_id;
|
|
456
|
+
const env = row.infisical.env;
|
|
457
|
+
const installInfi = execCapture('sprite', [
|
|
458
|
+
'exec', '-s', row.sprite, '--', 'bash', '-lc',
|
|
459
|
+
'command -v infisical || (curl -1sLf "https://artifacts-cli.infisical.com/setup.deb.sh" | sudo -E bash >/dev/null 2>&1 && sudo apt-get install -y infisical >/dev/null 2>&1) && infisical --version'
|
|
460
|
+
]);
|
|
461
|
+
teamResults.push({ step: 'infisical-install', ...installInfi });
|
|
462
|
+
const writeCreds = execCapture('sprite', [
|
|
463
|
+
'exec', '-s', row.sprite,
|
|
464
|
+
'--env', `RRR_INFI_CID=${cid},RRR_INFI_CS=${cs},RRR_INFI_PROJ=${proj},RRR_INFI_ENV=${env}`,
|
|
465
|
+
'--', 'bash', '-lc',
|
|
466
|
+
'umask 077 && cat > ~/.infisical-machine-creds <<EOF\nexport RRR_INFI_CID="$RRR_INFI_CID"\nexport RRR_INFI_CS="$RRR_INFI_CS"\nexport RRR_INFI_PROJ="$RRR_INFI_PROJ"\nexport RRR_INFI_ENV="$RRR_INFI_ENV"\nEOF'
|
|
467
|
+
]);
|
|
468
|
+
teamResults.push({ step: 'infisical-creds', ...writeCreds });
|
|
469
|
+
const writeLoader = execCapture('sprite', [
|
|
470
|
+
'exec', '-s', row.sprite, '--', 'bash', '-lc',
|
|
471
|
+
'cat > ~/.infisical-load.sh <<\'LOADER\'\n[ -f ~/.infisical-machine-creds ] && source ~/.infisical-machine-creds\nif [ -n "$RRR_INFI_CID" ] && [ -n "$RRR_INFI_CS" ]; then\n _IT=$(infisical login --method=universal-auth --client-id="$RRR_INFI_CID" --client-secret="$RRR_INFI_CS" --plain --silent 2>/dev/null)\n if [ -n "$_IT" ]; then\n export ANTHROPIC_API_KEY=$(infisical secrets get ANTHROPIC_API_KEY --token="$_IT" --projectId="$RRR_INFI_PROJ" --env="${RRR_INFI_ENV:-dev}" --plain --silent 2>/dev/null)\n unset _IT\n fi\nfi\nLOADER\ngrep -q "infisical-load.sh" ~/.profile || echo "[ -f ~/.infisical-load.sh ] && source ~/.infisical-load.sh" >> ~/.profile'
|
|
472
|
+
]);
|
|
473
|
+
teamResults.push({ step: 'infisical-loader', ...writeLoader });
|
|
474
|
+
} else if (row.infisical) {
|
|
475
|
+
teamResults.push({ step: 'infisical-skip', ok: false, output: 'infisical bootstrap requested but RRR_INFI_CID/RRR_INFI_CS not set in apply environment' });
|
|
476
|
+
}
|
|
419
477
|
const readyForCheckpoint = teamResults.every(result => result.ok);
|
|
420
478
|
const checkpoint = readyForCheckpoint
|
|
421
479
|
? execCapture('sprite', [
|
|
@@ -539,7 +597,32 @@ function buildAiGatewayBlock(opts) {
|
|
|
539
597
|
}
|
|
540
598
|
|
|
541
599
|
function buildAuthBlock(opts) {
|
|
542
|
-
const mode = opts.authMode || '
|
|
600
|
+
const mode = opts.authMode || 'infisical-machine-identity';
|
|
601
|
+
if (mode === 'infisical-machine-identity') {
|
|
602
|
+
const projectId = opts.infisicalProjectId || null;
|
|
603
|
+
const env = opts.infisicalEnv || 'dev';
|
|
604
|
+
const domain = opts.infisicalDomain || 'https://app.infisical.com';
|
|
605
|
+
const secrets = opts.infisicalSecrets && opts.infisicalSecrets.length > 0
|
|
606
|
+
? opts.infisicalSecrets
|
|
607
|
+
: ['ANTHROPIC_API_KEY', 'GITHUB_TOKEN'];
|
|
608
|
+
return {
|
|
609
|
+
mode,
|
|
610
|
+
description: 'Coordinator fetches secrets at boot from Infisical via Universal Auth (machine identity).',
|
|
611
|
+
infisical: {
|
|
612
|
+
domain,
|
|
613
|
+
project_id: projectId,
|
|
614
|
+
env,
|
|
615
|
+
secrets
|
|
616
|
+
},
|
|
617
|
+
required_runtime_env: ['RRR_INFI_CID', 'RRR_INFI_CS'],
|
|
618
|
+
required_secrets: ['RRR_INFI_CID', 'RRR_INFI_CS'],
|
|
619
|
+
notes: [
|
|
620
|
+
'RRR_INFI_CID + RRR_INFI_CS are Infisical Universal Auth client credentials (stored as Cloudflare Secrets / Wrangler secrets).',
|
|
621
|
+
'Boot script calls `infisical login --method=universal-auth` then `infisical secrets get` for each declared secret.',
|
|
622
|
+
'Anthropic, GitHub, and any future tokens rotate centrally in Infisical with no coordinator redeploy.'
|
|
623
|
+
]
|
|
624
|
+
};
|
|
625
|
+
}
|
|
543
626
|
if (mode === 'claude-code-subscription') {
|
|
544
627
|
return {
|
|
545
628
|
mode,
|
|
@@ -626,7 +709,38 @@ function writeCoordinatorProvision(options) {
|
|
|
626
709
|
auth.description
|
|
627
710
|
];
|
|
628
711
|
|
|
629
|
-
if (auth.mode === '
|
|
712
|
+
if (auth.mode === 'infisical-machine-identity') {
|
|
713
|
+
const infi = auth.infisical;
|
|
714
|
+
const fetches = (infi.secrets || []).map(s =>
|
|
715
|
+
`export ${s}=$(infisical secrets get ${s} --token="$_IT" --projectId="${infi.project_id || '<INFISICAL_PROJECT_ID>'}" --env="${infi.env}" --plain --silent)`
|
|
716
|
+
);
|
|
717
|
+
readme.push(
|
|
718
|
+
'',
|
|
719
|
+
'### Infisical machine-identity bootstrap',
|
|
720
|
+
'',
|
|
721
|
+
`- Domain: ${infi.domain}`,
|
|
722
|
+
`- Project ID: ${infi.project_id || '(unset — pass --infisical-project-id)'}`,
|
|
723
|
+
`- Env: ${infi.env}`,
|
|
724
|
+
`- Fetched secrets: ${(infi.secrets || []).join(', ') || '(none)'}`,
|
|
725
|
+
'',
|
|
726
|
+
'Cloudflare sandbox boot snippet:',
|
|
727
|
+
'',
|
|
728
|
+
'```bash',
|
|
729
|
+
'# Install Infisical CLI (one-time, ideally baked into image)',
|
|
730
|
+
'curl -1sLf "https://artifacts-cli.infisical.com/setup.deb.sh" | sudo -E bash',
|
|
731
|
+
'sudo apt-get install -y infisical',
|
|
732
|
+
'',
|
|
733
|
+
'# Exchange machine-identity credentials for an access token',
|
|
734
|
+
'_IT=$(infisical login --method=universal-auth --client-id="$RRR_INFI_CID" --client-secret="$RRR_INFI_CS" --plain --silent)',
|
|
735
|
+
'',
|
|
736
|
+
'# Fetch and export each declared secret',
|
|
737
|
+
...fetches,
|
|
738
|
+
'unset _IT',
|
|
739
|
+
'',
|
|
740
|
+
'claude --version # picks up ANTHROPIC_API_KEY automatically',
|
|
741
|
+
'```'
|
|
742
|
+
);
|
|
743
|
+
} else if (auth.mode === 'claude-code-subscription') {
|
|
630
744
|
readme.push(
|
|
631
745
|
'',
|
|
632
746
|
'### Credentials injection',
|
|
@@ -702,8 +816,25 @@ function readCoordinatorStatus(projectRoot) {
|
|
|
702
816
|
checks.push({ name: 'wrangler available', ok: wrangler.ok, detail: wrangler.output || 'not available' });
|
|
703
817
|
checks.push({ name: 'GITHUB_TOKEN present', ok: !!process.env.GITHUB_TOKEN, detail: process.env.GITHUB_TOKEN ? 'present' : 'missing locally' });
|
|
704
818
|
|
|
705
|
-
const authMode = config && config.auth ? config.auth.mode : (config && config.ai_gateway ? 'anthropic-api-key' : '
|
|
706
|
-
if (authMode === '
|
|
819
|
+
const authMode = config && config.auth ? config.auth.mode : (config && config.ai_gateway ? 'anthropic-api-key' : 'infisical-machine-identity');
|
|
820
|
+
if (authMode === 'infisical-machine-identity') {
|
|
821
|
+
checks.push({
|
|
822
|
+
name: 'RRR_INFI_CID present',
|
|
823
|
+
ok: !!process.env.RRR_INFI_CID,
|
|
824
|
+
detail: process.env.RRR_INFI_CID ? 'present' : 'missing locally (Infisical Universal Auth client ID)'
|
|
825
|
+
});
|
|
826
|
+
checks.push({
|
|
827
|
+
name: 'RRR_INFI_CS present',
|
|
828
|
+
ok: !!process.env.RRR_INFI_CS,
|
|
829
|
+
detail: process.env.RRR_INFI_CS ? 'present' : 'missing locally (Infisical Universal Auth client secret)'
|
|
830
|
+
});
|
|
831
|
+
const infiCli = execCapture('infisical', ['--version']);
|
|
832
|
+
checks.push({
|
|
833
|
+
name: 'infisical CLI available',
|
|
834
|
+
ok: infiCli.ok,
|
|
835
|
+
detail: infiCli.output || 'not installed locally; coordinator runtime must install it'
|
|
836
|
+
});
|
|
837
|
+
} else if (authMode === 'claude-code-subscription') {
|
|
707
838
|
const credsPath = path.join(os.homedir(), '.claude', '.credentials.json');
|
|
708
839
|
const localCreds = fs.existsSync(credsPath);
|
|
709
840
|
checks.push({
|
package/scripts/rrr-team-mode.js
CHANGED
|
@@ -189,7 +189,9 @@ function main() {
|
|
|
189
189
|
if (command === 'provision-sprites') {
|
|
190
190
|
const options = {
|
|
191
191
|
teams: args.teams,
|
|
192
|
-
team: args.team
|
|
192
|
+
team: args.team,
|
|
193
|
+
infisicalProjectId: args['infisical-project-id'],
|
|
194
|
+
infisicalEnv: args['infisical-env']
|
|
193
195
|
};
|
|
194
196
|
if (args.apply === true || args.apply === 'true') {
|
|
195
197
|
const result = applySpriteProvisionPlan(projectRoot, options);
|
|
@@ -226,6 +228,10 @@ function main() {
|
|
|
226
228
|
provider: args.provider,
|
|
227
229
|
name: args.name,
|
|
228
230
|
authMode: args['auth-mode'] || args.authMode,
|
|
231
|
+
infisicalProjectId: args['infisical-project-id'],
|
|
232
|
+
infisicalEnv: args['infisical-env'],
|
|
233
|
+
infisicalDomain: args['infisical-domain'],
|
|
234
|
+
infisicalSecrets: splitCsv(args['infisical-secrets']),
|
|
229
235
|
aiGatewayAccountId: args['ai-gateway-account-id'] || args['cf-account-id'],
|
|
230
236
|
aiGatewayId: args['ai-gateway-id'],
|
|
231
237
|
aiGatewayAuthenticated: args['ai-gateway-authenticated'] === true || args['ai-gateway-authenticated'] === 'true'
|
|
@@ -233,6 +239,9 @@ function main() {
|
|
|
233
239
|
console.log(`Wrote ${path.relative(projectRoot, path.join(result.dir, 'COORDINATOR.json'))}`);
|
|
234
240
|
console.log(`Wrote ${path.relative(projectRoot, path.join(result.dir, 'README.md'))}`);
|
|
235
241
|
console.log(`Auth mode: ${result.config.auth.mode}`);
|
|
242
|
+
if (result.config.auth && result.config.auth.infisical) {
|
|
243
|
+
console.log(`Infisical: ${result.config.auth.infisical.project_id || '(unset)'}@${result.config.auth.infisical.env}`);
|
|
244
|
+
}
|
|
236
245
|
if (result.config.ai_gateway) {
|
|
237
246
|
console.log(`AI Gateway: ${result.config.ai_gateway.anthropic_base_url}`);
|
|
238
247
|
}
|