specrails-core 4.1.0 → 4.2.0

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 (67) hide show
  1. package/README.md +4 -4
  2. package/VERSION +1 -1
  3. package/bin/specrails-core.mjs +302 -0
  4. package/commands/doctor.md +5 -5
  5. package/commands/enrich.md +9 -9
  6. package/dist/installer/cli.js +167 -0
  7. package/dist/installer/cli.js.map +1 -0
  8. package/dist/installer/commands/doctor.js +144 -0
  9. package/dist/installer/commands/doctor.js.map +1 -0
  10. package/dist/installer/commands/init.js +182 -0
  11. package/dist/installer/commands/init.js.map +1 -0
  12. package/dist/installer/commands/perf-check.js +16 -0
  13. package/dist/installer/commands/perf-check.js.map +1 -0
  14. package/dist/installer/commands/update.js +170 -0
  15. package/dist/installer/commands/update.js.map +1 -0
  16. package/dist/installer/phases/install-config.js +120 -0
  17. package/dist/installer/phases/install-config.js.map +1 -0
  18. package/dist/installer/phases/manifest.js +93 -0
  19. package/dist/installer/phases/manifest.js.map +1 -0
  20. package/dist/installer/phases/prereqs.js +116 -0
  21. package/dist/installer/phases/prereqs.js.map +1 -0
  22. package/dist/installer/phases/provider-detect.js +111 -0
  23. package/dist/installer/phases/provider-detect.js.map +1 -0
  24. package/dist/installer/phases/scaffold.js +373 -0
  25. package/dist/installer/phases/scaffold.js.map +1 -0
  26. package/dist/installer/util/errors.js +79 -0
  27. package/dist/installer/util/errors.js.map +1 -0
  28. package/dist/installer/util/exec.js +151 -0
  29. package/dist/installer/util/exec.js.map +1 -0
  30. package/dist/installer/util/fs.js +153 -0
  31. package/dist/installer/util/fs.js.map +1 -0
  32. package/dist/installer/util/git.js +113 -0
  33. package/dist/installer/util/git.js.map +1 -0
  34. package/dist/installer/util/logger.js +55 -0
  35. package/dist/installer/util/logger.js.map +1 -0
  36. package/dist/installer/util/paths.js +66 -0
  37. package/dist/installer/util/paths.js.map +1 -0
  38. package/dist/installer/util/prompts.js +49 -0
  39. package/dist/installer/util/prompts.js.map +1 -0
  40. package/dist/installer/util/template.js +60 -0
  41. package/dist/installer/util/template.js.map +1 -0
  42. package/docs/deployment.md +2 -1
  43. package/docs/installation.md +6 -3
  44. package/docs/testing/test-matrix-codex.md +19 -11
  45. package/docs/updating.md +24 -49
  46. package/docs/user-docs/faq.md +1 -1
  47. package/docs/windows.md +53 -0
  48. package/{templates/settings/integration-contract.json → integration-contract.json} +2 -2
  49. package/package.json +25 -10
  50. package/pinned-versions.json +4 -0
  51. package/schemas/profile.v1.json +11 -3
  52. package/templates/agents/sr-architect.md +1 -1
  53. package/templates/agents/sr-reviewer.md +1 -1
  54. package/templates/commands/specrails/compat-check.md +3 -3
  55. package/templates/commands/specrails/doctor.md +5 -5
  56. package/templates/commands/specrails/enrich.md +9 -9
  57. package/templates/commands/specrails/reconfig.md +2 -2
  58. package/templates/commands/specrails/refactor-recommender.md +2 -2
  59. package/templates/commands/specrails/vpc-drift.md +1 -1
  60. package/templates/skills/sr-compat-check/SKILL.md +3 -3
  61. package/templates/skills/sr-refactor-recommender/SKILL.md +2 -2
  62. package/bin/doctor.sh +0 -127
  63. package/bin/perf-check.sh +0 -21
  64. package/bin/specrails-core.js +0 -262
  65. package/commands/setup.md +0 -1461
  66. package/install.sh +0 -1231
  67. package/update.sh +0 -870
@@ -0,0 +1,120 @@
1
+ import path from 'node:path';
2
+ import yaml from 'js-yaml';
3
+ import { FilesystemError, InstallerError } from '../util/errors.js';
4
+ import { pathExists, readTextFile, writeFileLf } from '../util/fs.js';
5
+ export class InvalidConfigError extends InstallerError {
6
+ errors;
7
+ constructor(errors) {
8
+ super(`install-config.yaml is invalid:\n${errors.map((e) => ` - ${e}`).join('\n')}`, 40);
9
+ this.name = 'InvalidConfigError';
10
+ this.errors = errors;
11
+ }
12
+ }
13
+ export const CONFIG_RELATIVE_PATH = '.specrails/install-config.yaml';
14
+ /**
15
+ * Resolves the install-config path: explicit argument > repo-root default.
16
+ */
17
+ export function resolveConfigPath(repoRoot, explicit) {
18
+ if (explicit && explicit.length > 0) {
19
+ return path.isAbsolute(explicit) ? explicit : path.resolve(repoRoot, explicit);
20
+ }
21
+ return path.join(repoRoot, CONFIG_RELATIVE_PATH);
22
+ }
23
+ /**
24
+ * Reads and parses the install-config. Returns `null` if the file is
25
+ * not present — caller decides whether that's an error or a fallback.
26
+ */
27
+ export function loadInstallConfig(configPath) {
28
+ if (!pathExists(configPath))
29
+ return null;
30
+ const raw = readTextFile(configPath);
31
+ let parsed;
32
+ try {
33
+ parsed = yaml.load(raw);
34
+ }
35
+ catch (err) {
36
+ throw new InvalidConfigError([`YAML parse error: ${err.message}`]);
37
+ }
38
+ return validateInstallConfig(parsed);
39
+ }
40
+ /**
41
+ * Validates an already-parsed YAML document. Collects every error
42
+ * before throwing so the user sees them all at once (matches the
43
+ * bash installer's `_config_errors` accumulator behaviour).
44
+ */
45
+ export function validateInstallConfig(raw) {
46
+ const errors = [];
47
+ if (raw === null || raw === undefined || typeof raw !== 'object') {
48
+ throw new InvalidConfigError(['config must be a YAML mapping']);
49
+ }
50
+ const doc = raw;
51
+ if (doc.version === undefined) {
52
+ errors.push(`missing required 'version' field`);
53
+ }
54
+ else if (doc.version !== 1) {
55
+ errors.push(`unsupported version '${String(doc.version)}' (expected: 1)`);
56
+ }
57
+ if (doc.provider === undefined) {
58
+ errors.push(`missing required 'provider' field`);
59
+ }
60
+ else if (doc.provider === 'codex') {
61
+ errors.push(`Codex (OpenAI) support is coming soon — currently being tested in our lab. Set provider: claude.`);
62
+ }
63
+ else if (doc.provider !== 'claude') {
64
+ errors.push(`unsupported provider '${String(doc.provider)}' (expected: claude)`);
65
+ }
66
+ if (doc.tier !== undefined && doc.tier !== 'full' && doc.tier !== 'quick') {
67
+ errors.push(`unsupported tier '${String(doc.tier)}' (expected: full or quick)`);
68
+ }
69
+ const agents = doc.agents;
70
+ if (!agents || typeof agents !== 'object') {
71
+ errors.push(`missing required 'agents' section with 'selected' list`);
72
+ }
73
+ else {
74
+ if (!Array.isArray(agents.selected)) {
75
+ errors.push(`'agents.selected' must be a list`);
76
+ }
77
+ if (agents.preset !== undefined &&
78
+ agents.preset !== 'balanced' &&
79
+ agents.preset !== 'budget' &&
80
+ agents.preset !== 'max') {
81
+ errors.push(`unsupported preset '${String(agents.preset)}' (expected: balanced, budget, or max)`);
82
+ }
83
+ }
84
+ if (errors.length > 0) {
85
+ throw new InvalidConfigError(errors);
86
+ }
87
+ const selected = agents?.selected ?? [];
88
+ const result = {
89
+ version: 1,
90
+ provider: doc.provider,
91
+ agents: {
92
+ selected,
93
+ },
94
+ };
95
+ if (typeof doc.agent_teams === 'boolean') {
96
+ result.agent_teams = doc.agent_teams;
97
+ }
98
+ if (doc.tier !== undefined) {
99
+ result.tier = doc.tier;
100
+ }
101
+ if (agents?.preset !== undefined) {
102
+ result.agents.preset = agents.preset;
103
+ }
104
+ return result;
105
+ }
106
+ /**
107
+ * Serialises an {@link InstallConfig} back to YAML and writes it with
108
+ * LF line endings. Used by `npx specrails-core update --reset-config`
109
+ * paths (not wired yet) and by tests.
110
+ */
111
+ export function writeInstallConfig(configPath, config) {
112
+ try {
113
+ const text = yaml.dump(config, { lineWidth: 120, noRefs: true });
114
+ writeFileLf(configPath, text);
115
+ }
116
+ catch (err) {
117
+ throw new FilesystemError(`failed to write install-config.yaml: ${err.message}`, configPath);
118
+ }
119
+ }
120
+ //# sourceMappingURL=install-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-config.js","sourceRoot":"","sources":["../../../src/installer/phases/install-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readdirSync, statSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { FilesystemError } from '../util/errors.js';
5
+ import { readBytes, writeFileLf } from '../util/fs.js';
6
+ /**
7
+ * Computes a stable sha256 digest for a file, returned as `sha256:<hex>`.
8
+ */
9
+ export function sha256Of(filePath) {
10
+ const bytes = readBytes(filePath);
11
+ const hash = createHash('sha256').update(bytes).digest('hex');
12
+ return `sha256:${hash}`;
13
+ }
14
+ /**
15
+ * Walks `templates/**` plus the two bundled command files and
16
+ * produces a stable-sorted manifest. Replicates the output format of
17
+ * the retired `generate_manifest` bash helper so the byte-diff CI
18
+ * fixture passes without modification.
19
+ *
20
+ * Stable-sort rule: artifact keys are sorted ascending by POSIX path.
21
+ */
22
+ export function buildManifest(input) {
23
+ const installed_at = input.installedAt ?? new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
24
+ const artifacts = {};
25
+ const templatesDir = path.join(input.scriptDir, 'templates');
26
+ walkManifestSources(templatesDir).forEach((absFile) => {
27
+ const rel = path.relative(input.scriptDir, absFile).split(path.sep).join('/');
28
+ artifacts[rel] = sha256Of(absFile);
29
+ });
30
+ const enrichPath = path.join(input.scriptDir, 'commands', 'enrich.md');
31
+ const doctorPath = path.join(input.scriptDir, 'commands', 'doctor.md');
32
+ artifacts['commands/specrails/enrich.md'] = sha256Of(enrichPath);
33
+ artifacts['commands/specrails/doctor.md'] = sha256Of(doctorPath);
34
+ return {
35
+ version: input.version,
36
+ installed_at,
37
+ artifacts: sortKeys(artifacts),
38
+ };
39
+ }
40
+ /**
41
+ * Writes the manifest JSON and the companion version file under
42
+ * `.specrails/` in the user repo. Both files use LF terminators.
43
+ */
44
+ export function writeManifestFiles(repoRoot, manifest) {
45
+ const manifestPath = path.join(repoRoot, '.specrails', 'specrails-manifest.json');
46
+ const versionPath = path.join(repoRoot, '.specrails', 'specrails-version');
47
+ writeFileLf(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
48
+ writeFileLf(versionPath, `${manifest.version}\n`);
49
+ return { manifestPath, versionPath };
50
+ }
51
+ /**
52
+ * Enumerates files under `templates/` excluding `node_modules/` and
53
+ * package-lock files, matching the bash helper's find clause:
54
+ * find ... -not -path "(star)/node_modules/(star)" -not -name 'package-lock.json'
55
+ */
56
+ function walkManifestSources(root) {
57
+ const collected = [];
58
+ try {
59
+ walk(root, collected);
60
+ }
61
+ catch (err) {
62
+ throw new FilesystemError(`failed to walk templates directory: ${err.message}`, root);
63
+ }
64
+ // Stable sort on POSIX-normalised relative path ensures deterministic
65
+ // output across operating systems (readdir order is FS-dependent).
66
+ collected.sort();
67
+ return collected;
68
+ }
69
+ function walk(dir, acc) {
70
+ const entries = readdirSync(dir);
71
+ for (const name of entries) {
72
+ const abs = path.join(dir, name);
73
+ const st = statSync(abs);
74
+ if (st.isDirectory()) {
75
+ if (name === 'node_modules')
76
+ continue;
77
+ walk(abs, acc);
78
+ }
79
+ else if (st.isFile()) {
80
+ if (name === 'package-lock.json')
81
+ continue;
82
+ acc.push(abs);
83
+ }
84
+ }
85
+ }
86
+ function sortKeys(obj) {
87
+ const out = {};
88
+ for (const k of Object.keys(obj).sort()) {
89
+ out[k] = obj[k];
90
+ }
91
+ return out;
92
+ }
93
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/installer/phases/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ import path from 'node:path';
2
+ import { PrerequisiteError } from '../util/errors.js';
3
+ import { commandExists, runCommand } from '../util/exec.js';
4
+ import { isDir, isFile, listDir } from '../util/fs.js';
5
+ import { info, ok, warn } from '../util/logger.js';
6
+ import { gitInstalled, initRepo, isGitRepo, repoRoot } from '../util/git.js';
7
+ import { assertClaudeAuthenticated, claudeVersion, detectAvailability, resolveProvider, } from './provider-detect.js';
8
+ /**
9
+ * Orchestrates every prerequisite check that must pass before the
10
+ * installer proceeds to Phase 3 (scaffolding). Emits ok/warn/info
11
+ * lines matching the retired bash output.
12
+ */
13
+ export async function checkPrerequisites(options) {
14
+ if (!(await gitInstalled())) {
15
+ throw new PrerequisiteError('git is required but not on PATH. Install git: https://git-scm.com/');
16
+ }
17
+ // 1.1 Git repository — auto-init when --yes, otherwise assume caller
18
+ // resolved the prompt upstream (bin/specrails-core.cjs / hub TUI).
19
+ if (!(await isGitRepo(options.repoRoot))) {
20
+ if (!options.autoYes) {
21
+ throw new PrerequisiteError(`${options.repoRoot} is not a git repository. ` +
22
+ `Run \`git init\` first, or pass --yes to let the installer initialise it.`);
23
+ }
24
+ await initRepo(options.repoRoot);
25
+ }
26
+ const root = await repoRoot(options.repoRoot);
27
+ ok(`Git repository root: ${root}`);
28
+ // 1.2 Provider detection.
29
+ const availability = await detectAvailability();
30
+ const provider = await resolveProvider(availability, {
31
+ explicit: options.explicitProvider,
32
+ skipPrereqs: options.skipPrereqs,
33
+ });
34
+ if (provider === 'claude') {
35
+ if (availability.claude) {
36
+ const v = await claudeVersion();
37
+ ok(`Claude Code CLI: ${v}`);
38
+ }
39
+ else if (options.explicitProvider === 'claude') {
40
+ ok(`Provider: claude (--provider flag)`);
41
+ }
42
+ }
43
+ // 1.3 Authentication for the selected provider.
44
+ if (provider === 'claude') {
45
+ await assertClaudeAuthenticated({ skipPrereqs: options.skipPrereqs });
46
+ ok('Claude: authenticated');
47
+ }
48
+ // 1.4 npm — required for running the TUI and for the `update` command.
49
+ if (!(await commandExists('npm'))) {
50
+ if (options.skipPrereqs) {
51
+ warn('npm not found (skipped — SPECRAILS_SKIP_PREREQS=1)');
52
+ }
53
+ else {
54
+ throw new PrerequisiteError('npm is required but not on PATH. Install Node.js 20+ from https://nodejs.org/');
55
+ }
56
+ }
57
+ else {
58
+ ok('npm: found');
59
+ }
60
+ // 1.5 OpenSpec CLI — optional. The installer falls back to
61
+ // `npx openspec` automatically, so a missing global install is
62
+ // not a problem. Warn only when present is preferable (faster).
63
+ if (await commandExists('openspec')) {
64
+ ok('OpenSpec CLI: found (global)');
65
+ }
66
+ else {
67
+ info('OpenSpec CLI not on PATH — will fetch via npx during install');
68
+ }
69
+ // 1.6 GitHub CLI — optional; enables OSS detection + issue-backed flows.
70
+ const hasGh = await commandExists('gh');
71
+ if (hasGh) {
72
+ ok('GitHub CLI: found');
73
+ }
74
+ else {
75
+ info('GitHub CLI not found — optional, enables OSS detection');
76
+ }
77
+ // 1.7 OSS detection — runs only when gh is present + authenticated.
78
+ // Degrades gracefully (every signal independently false) when
79
+ // prereqs are missing.
80
+ const ossSignals = await detectOssSignals(options.repoRoot, hasGh);
81
+ if (ossSignals.isOss) {
82
+ ok('OSS project detected (public repo + CI + CONTRIBUTING.md)');
83
+ }
84
+ // 1.8 JIRA CLI — silently skipped when missing (only relevant in enrich).
85
+ return { availability, provider, ossSignals };
86
+ }
87
+ async function detectOssSignals(repoRoot, hasGh) {
88
+ let publicRepo = false;
89
+ if (hasGh) {
90
+ try {
91
+ const { stdout } = await runCommand('gh', ['repo', 'view', '--json', 'isPrivate', '--jq', '.isPrivate'], { cwd: repoRoot, inherit: false });
92
+ // gh emits 'true' / 'false' newline-terminated.
93
+ publicRepo = stdout.trim().toLowerCase() === 'false';
94
+ }
95
+ catch {
96
+ /* not a gh-tracked repo or gh not authenticated — leave false */
97
+ }
98
+ }
99
+ const hasCi = hasCiWorkflows(repoRoot);
100
+ const hasContributing = isFile(path.join(repoRoot, 'CONTRIBUTING.md')) ||
101
+ isFile(path.join(repoRoot, '.github', 'CONTRIBUTING.md'));
102
+ return {
103
+ hasGh,
104
+ publicRepo,
105
+ hasCi,
106
+ hasContributing,
107
+ isOss: hasGh && publicRepo && hasCi && hasContributing,
108
+ };
109
+ }
110
+ function hasCiWorkflows(repoRoot) {
111
+ const dir = path.join(repoRoot, '.github', 'workflows');
112
+ if (!isDir(dir))
113
+ return false;
114
+ return listDir(dir).some((p) => p.endsWith('.yml') || p.endsWith('.yaml'));
115
+ }
116
+ //# sourceMappingURL=prereqs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prereqs.js","sourceRoot":"","sources":["../../../src/installer/phases/prereqs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ import { homedir } from 'node:os';
2
+ import path from 'node:path';
3
+ import { ProviderError } from '../util/errors.js';
4
+ import { commandExists, runCommand } from '../util/exec.js';
5
+ import { pathExists, readTextFile } from '../util/fs.js';
6
+ /**
7
+ * Detects which AI CLIs are on PATH. Runs `where` on Windows and
8
+ * `which` on POSIX via the cross-platform commandExists helper.
9
+ */
10
+ export async function detectAvailability() {
11
+ const [claude, codex] = await Promise.all([commandExists('claude'), commandExists('codex')]);
12
+ return { claude, codex };
13
+ }
14
+ /**
15
+ * Returns the Claude CLI version string (stdout of `claude --version`)
16
+ * or 'unknown' if the CLI cannot be invoked.
17
+ */
18
+ export async function claudeVersion() {
19
+ try {
20
+ const { stdout } = await runCommand('claude', ['--version'], { inherit: false });
21
+ return stdout.trim() || 'unknown';
22
+ }
23
+ catch {
24
+ return 'unknown';
25
+ }
26
+ }
27
+ /**
28
+ * Resolves which provider the installer should use. Priority:
29
+ * 1. Explicit `--provider claude` flag (already passed through args).
30
+ * 2. Config file (read upstream in install-config.ts).
31
+ * 3. Whichever CLI is installed.
32
+ *
33
+ * Codex-only installs error out — see design: Codex is "coming soon".
34
+ * The caller may bypass prereq failures with `skipPrereqs: true`
35
+ * (env `SPECRAILS_SKIP_PREREQS=1`).
36
+ */
37
+ export async function resolveProvider(availability, options = {}) {
38
+ if (options.explicit === 'codex') {
39
+ throw new ProviderError('Codex (OpenAI) support is coming soon — currently being tested in our lab. ' +
40
+ 'Use --provider claude for now.');
41
+ }
42
+ if (options.explicit === 'claude')
43
+ return 'claude';
44
+ if (availability.claude && availability.codex)
45
+ return 'claude';
46
+ if (availability.claude)
47
+ return 'claude';
48
+ if (availability.codex) {
49
+ throw new ProviderError('Only Codex detected — Codex (OpenAI) support is coming soon (currently in our lab). ' +
50
+ 'Please install Claude Code to continue: https://claude.ai/download');
51
+ }
52
+ if (options.skipPrereqs)
53
+ return 'claude';
54
+ throw new ProviderError('No AI CLI found (claude). ' +
55
+ 'Install Claude Code: https://claude.ai/download. ' +
56
+ 'Codex (OpenAI) support: coming soon.');
57
+ }
58
+ /**
59
+ * Directory / filename conventions the provider dictates.
60
+ * - Claude Code: .claude/ + CLAUDE.md
61
+ * - Codex: .codex/ + AGENTS.md
62
+ */
63
+ export function derivedPaths(provider) {
64
+ if (provider === 'codex') {
65
+ return { providerDir: '.codex', instructionsFile: 'AGENTS.md' };
66
+ }
67
+ return { providerDir: '.claude', instructionsFile: 'CLAUDE.md' };
68
+ }
69
+ /**
70
+ * Asserts that Claude Code is authenticated. Matches the three-path
71
+ * check from install.sh: `claude config list` → ANTHROPIC_API_KEY env →
72
+ * ~/.claude.json OAuth.
73
+ */
74
+ export async function assertClaudeAuthenticated(options = {}) {
75
+ if (await hasClaudeApiKeyConfigured())
76
+ return;
77
+ if (process.env.ANTHROPIC_API_KEY && process.env.ANTHROPIC_API_KEY.length > 0)
78
+ return;
79
+ if (await hasClaudeOauthJson())
80
+ return;
81
+ if (options.skipPrereqs)
82
+ return;
83
+ throw new ProviderError([
84
+ 'No Claude authentication found.',
85
+ '',
86
+ ' Option 1 (API key): claude config set api_key <your-key>',
87
+ ' Option 2 (OAuth): claude auth login',
88
+ ].join('\n'));
89
+ }
90
+ async function hasClaudeApiKeyConfigured() {
91
+ try {
92
+ const { stdout } = await runCommand('claude', ['config', 'list'], { inherit: false });
93
+ return stdout.includes('api_key');
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ }
99
+ async function hasClaudeOauthJson() {
100
+ const p = path.join(homedir(), '.claude.json');
101
+ if (!pathExists(p))
102
+ return false;
103
+ try {
104
+ const raw = readTextFile(p);
105
+ return raw.includes('"oauthAccount"');
106
+ }
107
+ catch {
108
+ return false;
109
+ }
110
+ }
111
+ //# sourceMappingURL=provider-detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-detect.js","sourceRoot":"","sources":["../../../src/installer/phases/provider-detect.ts"],"names":[],"mappings":"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"}