dlw-machine-setup 0.3.3 → 0.4.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.
- package/bin/installer.js +115 -32
- package/package.json +1 -1
package/bin/installer.js
CHANGED
|
@@ -3276,44 +3276,87 @@ async function fetchWizardOptions(token, repo) {
|
|
|
3276
3276
|
// src/utils/data/discover-repo.ts
|
|
3277
3277
|
var GITHUB_ORG = "DLW-INT-SAP-DEV";
|
|
3278
3278
|
var REPO_TOPIC = "one-shot-data";
|
|
3279
|
+
var MAX_RETRIES = 3;
|
|
3280
|
+
var RETRY_DELAY_MS = 2e3;
|
|
3279
3281
|
async function discoverRepo(token) {
|
|
3280
3282
|
const headers = {
|
|
3281
3283
|
"Accept": "application/vnd.github+json",
|
|
3282
3284
|
"Authorization": `Bearer ${token}`
|
|
3283
3285
|
};
|
|
3284
|
-
const
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3286
|
+
const searchResult = await trySearchAPI(headers);
|
|
3287
|
+
if (searchResult) return searchResult;
|
|
3288
|
+
const listResult = await tryOrgReposList(headers);
|
|
3289
|
+
if (listResult) return listResult;
|
|
3290
|
+
throw new Error(
|
|
3291
|
+
"No data repository found. Verify your GitHub account has access to the organization and the repository topic is configured."
|
|
3292
|
+
);
|
|
3293
|
+
}
|
|
3294
|
+
async function trySearchAPI(headers) {
|
|
3295
|
+
try {
|
|
3296
|
+
const response = await fetchWithRetry(
|
|
3297
|
+
`https://api.github.com/search/repositories?q=topic:${REPO_TOPIC}+org:${GITHUB_ORG}`,
|
|
3289
3298
|
{ headers }
|
|
3290
3299
|
);
|
|
3291
|
-
if (!response.ok)
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3300
|
+
if (!response.ok) return null;
|
|
3301
|
+
const data = await response.json();
|
|
3302
|
+
if (data.total_count === 1) return data.items[0].full_name;
|
|
3303
|
+
if (data.total_count > 1) {
|
|
3304
|
+
throw new Error("Multiple data repositories found. Contact your administrator.");
|
|
3295
3305
|
}
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3306
|
+
return null;
|
|
3307
|
+
} catch (error) {
|
|
3308
|
+
if (error instanceof Error && error.message.includes("Multiple")) throw error;
|
|
3309
|
+
return null;
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
async function tryOrgReposList(headers) {
|
|
3313
|
+
try {
|
|
3314
|
+
const matched = [];
|
|
3315
|
+
let page = 1;
|
|
3316
|
+
while (true) {
|
|
3317
|
+
const response = await fetchWithRetry(
|
|
3318
|
+
`https://api.github.com/orgs/${GITHUB_ORG}/repos?type=all&per_page=100&page=${page}`,
|
|
3319
|
+
{ headers }
|
|
3320
|
+
);
|
|
3321
|
+
if (!response.ok) return null;
|
|
3322
|
+
const repos = await response.json();
|
|
3323
|
+
if (repos.length === 0) break;
|
|
3324
|
+
for (const repo of repos) {
|
|
3325
|
+
if (repo.topics?.includes(REPO_TOPIC)) {
|
|
3326
|
+
matched.push(repo.full_name);
|
|
3327
|
+
}
|
|
3301
3328
|
}
|
|
3329
|
+
if (repos.length < 100) break;
|
|
3330
|
+
page++;
|
|
3302
3331
|
}
|
|
3303
|
-
if (
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
);
|
|
3332
|
+
if (matched.length === 1) return matched[0];
|
|
3333
|
+
if (matched.length > 1) {
|
|
3334
|
+
throw new Error("Multiple data repositories found. Contact your administrator.");
|
|
3335
|
+
}
|
|
3336
|
+
return null;
|
|
3337
|
+
} catch (error) {
|
|
3338
|
+
if (error instanceof Error && error.message.includes("Multiple")) throw error;
|
|
3339
|
+
return null;
|
|
3310
3340
|
}
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3341
|
+
}
|
|
3342
|
+
async function fetchWithRetry(url, options) {
|
|
3343
|
+
let lastError = null;
|
|
3344
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
3345
|
+
try {
|
|
3346
|
+
const response = await fetch(url, options);
|
|
3347
|
+
if (!response.ok && response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
3348
|
+
return response;
|
|
3349
|
+
}
|
|
3350
|
+
if (response.ok) return response;
|
|
3351
|
+
lastError = new Error(`HTTP ${response.status}`);
|
|
3352
|
+
} catch (error) {
|
|
3353
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3354
|
+
}
|
|
3355
|
+
if (attempt < MAX_RETRIES) {
|
|
3356
|
+
await new Promise((resolve3) => setTimeout(resolve3, RETRY_DELAY_MS * Math.pow(2, attempt - 1)));
|
|
3357
|
+
}
|
|
3315
3358
|
}
|
|
3316
|
-
return
|
|
3359
|
+
return new Response(null, { status: 503, statusText: "Retry exhausted" });
|
|
3317
3360
|
}
|
|
3318
3361
|
|
|
3319
3362
|
// src/utils/data/fetch-contexts.ts
|
|
@@ -3477,8 +3520,8 @@ async function getGitHubToken() {
|
|
|
3477
3520
|
}
|
|
3478
3521
|
|
|
3479
3522
|
// src/utils/data/fetch-contexts.ts
|
|
3480
|
-
var
|
|
3481
|
-
var
|
|
3523
|
+
var MAX_RETRIES2 = 3;
|
|
3524
|
+
var RETRY_DELAY_MS2 = 2e3;
|
|
3482
3525
|
var MIN_FILE_SIZE = 1024;
|
|
3483
3526
|
async function fetchContexts(options = {}) {
|
|
3484
3527
|
const { domains = [], targetDir = process.cwd(), force = false, token, repo } = options;
|
|
@@ -3502,7 +3545,7 @@ async function fetchContexts(options = {}) {
|
|
|
3502
3545
|
"Accept": "application/vnd.github+json",
|
|
3503
3546
|
"Authorization": `Bearer ${githubToken}`
|
|
3504
3547
|
};
|
|
3505
|
-
const releaseResponse = await
|
|
3548
|
+
const releaseResponse = await fetchWithRetry2(releaseUrl, { headers }, MAX_RETRIES2);
|
|
3506
3549
|
if (!releaseResponse.ok) {
|
|
3507
3550
|
throw new Error(
|
|
3508
3551
|
`GitHub API error (${releaseResponse.status}): ${getReadableError(releaseResponse.status)}`
|
|
@@ -3540,7 +3583,7 @@ async function fetchContexts(options = {}) {
|
|
|
3540
3583
|
"Authorization": `Bearer ${githubToken}`
|
|
3541
3584
|
};
|
|
3542
3585
|
const downloadUrl = asset.url;
|
|
3543
|
-
const response = await
|
|
3586
|
+
const response = await fetchWithRetry2(downloadUrl, { headers: downloadHeaders }, MAX_RETRIES2);
|
|
3544
3587
|
if (!response.ok) {
|
|
3545
3588
|
throw new Error(`Download failed: ${getReadableError(response.status)}`);
|
|
3546
3589
|
}
|
|
@@ -3603,7 +3646,7 @@ async function checkPrerequisites() {
|
|
|
3603
3646
|
);
|
|
3604
3647
|
}
|
|
3605
3648
|
}
|
|
3606
|
-
async function
|
|
3649
|
+
async function fetchWithRetry2(url, options, maxRetries) {
|
|
3607
3650
|
let lastError = null;
|
|
3608
3651
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
3609
3652
|
try {
|
|
@@ -3619,7 +3662,7 @@ async function fetchWithRetry(url, options, maxRetries) {
|
|
|
3619
3662
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3620
3663
|
}
|
|
3621
3664
|
if (attempt < maxRetries) {
|
|
3622
|
-
const delay =
|
|
3665
|
+
const delay = RETRY_DELAY_MS2 * Math.pow(2, attempt - 1);
|
|
3623
3666
|
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
3624
3667
|
}
|
|
3625
3668
|
}
|
|
@@ -3740,6 +3783,7 @@ var import_path4 = require("path");
|
|
|
3740
3783
|
var CONTEXTS_DIR = (0, import_path4.join)(process.cwd(), "_ai-context");
|
|
3741
3784
|
var MARKER_START = "<!-- one-shot-installer:start -->";
|
|
3742
3785
|
var MARKER_END = "<!-- one-shot-installer:end -->";
|
|
3786
|
+
var SNIPPET_FILENAME = "context-instructions.md";
|
|
3743
3787
|
function upsertBlock(filePath, block) {
|
|
3744
3788
|
const marked = `${MARKER_START}
|
|
3745
3789
|
${block}
|
|
@@ -3759,6 +3803,36 @@ ${MARKER_END}`;
|
|
|
3759
3803
|
(0, import_fs4.writeFileSync)(filePath, existing + separator + marked, "utf-8");
|
|
3760
3804
|
}
|
|
3761
3805
|
}
|
|
3806
|
+
function downgradeHeadings(content) {
|
|
3807
|
+
let inCodeBlock = false;
|
|
3808
|
+
return content.split("\n").map((line) => {
|
|
3809
|
+
if (/^ {0,3}```/.test(line)) inCodeBlock = !inCodeBlock;
|
|
3810
|
+
if (inCodeBlock) return line;
|
|
3811
|
+
return line.replace(/^(#{1,5}) /, "$1# ");
|
|
3812
|
+
}).join("\n");
|
|
3813
|
+
}
|
|
3814
|
+
function buildWorkflowSection(domains) {
|
|
3815
|
+
const sections = [];
|
|
3816
|
+
for (const domain of domains) {
|
|
3817
|
+
const domainUpper = domain.toUpperCase();
|
|
3818
|
+
const snippetPath = (0, import_path4.join)(CONTEXTS_DIR, domainUpper, SNIPPET_FILENAME);
|
|
3819
|
+
if (!(0, import_fs4.existsSync)(snippetPath)) continue;
|
|
3820
|
+
const raw = (0, import_fs4.readFileSync)(snippetPath, "utf-8").trim();
|
|
3821
|
+
if (!raw) continue;
|
|
3822
|
+
sections.push(downgradeHeadings(raw));
|
|
3823
|
+
}
|
|
3824
|
+
return sections.length > 0 ? sections.join("\n\n") + "\n" : "";
|
|
3825
|
+
}
|
|
3826
|
+
function cleanupWorkflowSnippets(domains) {
|
|
3827
|
+
for (const domain of domains) {
|
|
3828
|
+
const domainUpper = domain.toUpperCase();
|
|
3829
|
+
const snippetPath = (0, import_path4.join)(CONTEXTS_DIR, domainUpper, SNIPPET_FILENAME);
|
|
3830
|
+
try {
|
|
3831
|
+
(0, import_fs4.unlinkSync)(snippetPath);
|
|
3832
|
+
} catch {
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3762
3836
|
function buildMCPSection(mcpConfig) {
|
|
3763
3837
|
const entries = Object.entries(mcpConfig);
|
|
3764
3838
|
if (entries.length === 0) return "";
|
|
@@ -3822,6 +3896,12 @@ function buildCombinedInstructions(domains, mcpConfig) {
|
|
|
3822
3896
|
if (mcpConfig && Object.keys(mcpConfig).length > 0) {
|
|
3823
3897
|
lines2.push(buildMCPSection(mcpConfig));
|
|
3824
3898
|
}
|
|
3899
|
+
const workflowContent = buildWorkflowSection(domains);
|
|
3900
|
+
if (workflowContent) {
|
|
3901
|
+
lines2.push(`## Domain-Specific Development Workflows
|
|
3902
|
+
`);
|
|
3903
|
+
lines2.push(workflowContent);
|
|
3904
|
+
}
|
|
3825
3905
|
return lines2.join("\n");
|
|
3826
3906
|
}
|
|
3827
3907
|
async function setupInstructions(config) {
|
|
@@ -3830,6 +3910,7 @@ async function setupInstructions(config) {
|
|
|
3830
3910
|
case "claude-code": {
|
|
3831
3911
|
const content = buildCombinedInstructions(domains, mcpConfig);
|
|
3832
3912
|
upsertBlock((0, import_path4.join)(process.cwd(), "CLAUDE.md"), content);
|
|
3913
|
+
cleanupWorkflowSnippets(domains);
|
|
3833
3914
|
break;
|
|
3834
3915
|
}
|
|
3835
3916
|
case "github-copilot": {
|
|
@@ -3842,6 +3923,7 @@ applyTo: "**"
|
|
|
3842
3923
|
|
|
3843
3924
|
${body}`;
|
|
3844
3925
|
upsertBlock((0, import_path4.join)(agentsDir, `instructions.md`), withFrontmatter);
|
|
3926
|
+
cleanupWorkflowSnippets(domains);
|
|
3845
3927
|
break;
|
|
3846
3928
|
}
|
|
3847
3929
|
case "cursor": {
|
|
@@ -3849,6 +3931,7 @@ ${body}`;
|
|
|
3849
3931
|
if (!(0, import_fs4.existsSync)(cursorDir)) (0, import_fs4.mkdirSync)(cursorDir, { recursive: true });
|
|
3850
3932
|
const body = buildCombinedInstructions(domains, mcpConfig);
|
|
3851
3933
|
upsertBlock((0, import_path4.join)(cursorDir, `instructions.mdc`), body);
|
|
3934
|
+
cleanupWorkflowSnippets(domains);
|
|
3852
3935
|
break;
|
|
3853
3936
|
}
|
|
3854
3937
|
default:
|