repowise 0.1.63 → 0.1.64
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/bin/repowise.js +212 -44
- package/package.json +1 -1
package/dist/bin/repowise.js
CHANGED
|
@@ -3,26 +3,28 @@
|
|
|
3
3
|
// bin/repowise.ts
|
|
4
4
|
import { readFileSync } from "fs";
|
|
5
5
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6
|
-
import { dirname as
|
|
6
|
+
import { dirname as dirname3, join as join15 } from "path";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
|
|
9
9
|
// ../listener/dist/main.js
|
|
10
|
-
import { readFile as readFile4, writeFile as
|
|
10
|
+
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir5 } from "fs/promises";
|
|
11
11
|
import { homedir as homedir4 } from "os";
|
|
12
12
|
import { join as join6 } from "path";
|
|
13
13
|
|
|
14
14
|
// ../listener/dist/lib/config.js
|
|
15
|
-
import { readFile } from "fs/promises";
|
|
15
|
+
import { readFile, writeFile, rename, mkdir, chmod } from "fs/promises";
|
|
16
16
|
import { homedir } from "os";
|
|
17
17
|
import { join } from "path";
|
|
18
18
|
var CONFIG_DIR = join(homedir(), ".repowise");
|
|
19
19
|
var CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
20
20
|
var DEFAULT_API_URL = "https://api.repowise.ai";
|
|
21
|
+
var rawConfigCache = null;
|
|
21
22
|
async function getListenerConfig() {
|
|
22
23
|
const apiUrl = process.env["REPOWISE_API_URL"] ?? DEFAULT_API_URL;
|
|
23
24
|
try {
|
|
24
25
|
const data = await readFile(CONFIG_PATH, "utf-8");
|
|
25
26
|
const raw = JSON.parse(data);
|
|
27
|
+
rawConfigCache = raw;
|
|
26
28
|
const validRepos = (raw.repos ?? []).filter((r) => typeof r === "object" && r !== null && typeof r.repoId === "string" && typeof r.localPath === "string");
|
|
27
29
|
return {
|
|
28
30
|
defaultApiUrl: raw.apiUrl ?? apiUrl,
|
|
@@ -32,9 +34,22 @@ async function getListenerConfig() {
|
|
|
32
34
|
return { defaultApiUrl: apiUrl, repos: [] };
|
|
33
35
|
}
|
|
34
36
|
}
|
|
37
|
+
async function saveListenerConfig(config2) {
|
|
38
|
+
await mkdir(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
39
|
+
const output = {
|
|
40
|
+
...rawConfigCache ?? {},
|
|
41
|
+
apiUrl: config2.defaultApiUrl,
|
|
42
|
+
repos: config2.repos
|
|
43
|
+
};
|
|
44
|
+
const tmpPath = CONFIG_PATH + ".tmp";
|
|
45
|
+
await writeFile(tmpPath, JSON.stringify(output, null, 2));
|
|
46
|
+
await chmod(tmpPath, 384);
|
|
47
|
+
await rename(tmpPath, CONFIG_PATH);
|
|
48
|
+
rawConfigCache = output;
|
|
49
|
+
}
|
|
35
50
|
|
|
36
51
|
// ../listener/dist/lib/state.js
|
|
37
|
-
import { readFile as readFile2, writeFile, mkdir, chmod } from "fs/promises";
|
|
52
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
38
53
|
import { homedir as homedir2 } from "os";
|
|
39
54
|
import { join as join2 } from "path";
|
|
40
55
|
var CONFIG_DIR2 = join2(homedir2(), ".repowise");
|
|
@@ -54,13 +69,109 @@ async function loadState() {
|
|
|
54
69
|
}
|
|
55
70
|
}
|
|
56
71
|
async function saveState(state) {
|
|
57
|
-
await
|
|
58
|
-
await
|
|
59
|
-
await
|
|
72
|
+
await mkdir2(CONFIG_DIR2, { recursive: true, mode: 448 });
|
|
73
|
+
await writeFile2(STATE_PATH, JSON.stringify(state, null, 2));
|
|
74
|
+
await chmod2(STATE_PATH, 384);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ../listener/dist/lib/reconcile.js
|
|
78
|
+
import { basename, dirname } from "path";
|
|
79
|
+
function reconcileRepos(configRepos, activeRepos, state, apiUrl) {
|
|
80
|
+
if (activeRepos.length === 0) {
|
|
81
|
+
return { updated: false, repos: configRepos, changes: [] };
|
|
82
|
+
}
|
|
83
|
+
const changes = [];
|
|
84
|
+
let updated = false;
|
|
85
|
+
const updatedRepos = configRepos.map((repo) => {
|
|
86
|
+
if (repo.apiUrl && repo.apiUrl !== apiUrl) {
|
|
87
|
+
return repo;
|
|
88
|
+
}
|
|
89
|
+
if (activeRepos.some((ar) => ar.repoId === repo.repoId)) {
|
|
90
|
+
if (!repo.platform || !repo.externalId) {
|
|
91
|
+
const match = activeRepos.find((ar) => ar.repoId === repo.repoId);
|
|
92
|
+
if (match) {
|
|
93
|
+
changes.push(`Backfilled platform+externalId for ${match.fullName} (${repo.repoId})`);
|
|
94
|
+
updated = true;
|
|
95
|
+
return {
|
|
96
|
+
...repo,
|
|
97
|
+
platform: match.platform,
|
|
98
|
+
externalId: match.externalId
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return repo;
|
|
103
|
+
}
|
|
104
|
+
if (repo.platform && repo.externalId) {
|
|
105
|
+
const match = activeRepos.find((ar) => ar.platform === repo.platform && ar.externalId === repo.externalId);
|
|
106
|
+
if (match) {
|
|
107
|
+
const oldId = repo.repoId;
|
|
108
|
+
changes.push(`Updated repoId for ${match.fullName}: ${oldId} \u2192 ${match.repoId}`);
|
|
109
|
+
migrateState(state, oldId, match.repoId);
|
|
110
|
+
updated = true;
|
|
111
|
+
return {
|
|
112
|
+
...repo,
|
|
113
|
+
repoId: match.repoId,
|
|
114
|
+
platform: match.platform,
|
|
115
|
+
externalId: match.externalId
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const dirName = basename(repo.localPath);
|
|
120
|
+
const parentDir = basename(dirname(repo.localPath));
|
|
121
|
+
const fullPathName = `${parentDir}/${dirName}`;
|
|
122
|
+
let matches = activeRepos.filter((ar) => ar.fullName === fullPathName);
|
|
123
|
+
if (matches.length === 0) {
|
|
124
|
+
matches = activeRepos.filter((ar) => ar.name === dirName);
|
|
125
|
+
}
|
|
126
|
+
if (matches.length === 1) {
|
|
127
|
+
const match = matches[0];
|
|
128
|
+
if (match.repoId !== repo.repoId) {
|
|
129
|
+
const oldId = repo.repoId;
|
|
130
|
+
changes.push(`Updated repoId for ${match.fullName} (name match): ${oldId} \u2192 ${match.repoId}`);
|
|
131
|
+
migrateState(state, oldId, match.repoId);
|
|
132
|
+
updated = true;
|
|
133
|
+
return {
|
|
134
|
+
...repo,
|
|
135
|
+
repoId: match.repoId,
|
|
136
|
+
platform: match.platform,
|
|
137
|
+
externalId: match.externalId
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (!repo.platform || !repo.externalId) {
|
|
141
|
+
changes.push(`Backfilled platform+externalId for ${match.fullName} (name match)`);
|
|
142
|
+
updated = true;
|
|
143
|
+
return {
|
|
144
|
+
...repo,
|
|
145
|
+
platform: match.platform,
|
|
146
|
+
externalId: match.externalId
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
} else if (matches.length > 1) {
|
|
150
|
+
console.warn(`Ambiguous name match for ${repo.localPath} \u2014 ${matches.length} repos match "${dirName}". Skipping auto-update. Add platform+externalId to config or re-run \`repowise create\`.`);
|
|
151
|
+
}
|
|
152
|
+
return repo;
|
|
153
|
+
});
|
|
154
|
+
return { updated, repos: updatedRepos, changes };
|
|
155
|
+
}
|
|
156
|
+
function migrateState(state, oldId, newId) {
|
|
157
|
+
const oldState = state.repos[oldId];
|
|
158
|
+
if (oldState) {
|
|
159
|
+
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
|
|
160
|
+
state.repos[newId] = {
|
|
161
|
+
lastSyncTimestamp: twentyFourHoursAgo,
|
|
162
|
+
lastSyncCommitSha: null
|
|
163
|
+
};
|
|
164
|
+
delete state.repos[oldId];
|
|
165
|
+
} else {
|
|
166
|
+
state.repos[newId] = {
|
|
167
|
+
lastSyncTimestamp: new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString(),
|
|
168
|
+
lastSyncCommitSha: null
|
|
169
|
+
};
|
|
170
|
+
}
|
|
60
171
|
}
|
|
61
172
|
|
|
62
173
|
// ../listener/dist/lib/auth.js
|
|
63
|
-
import { readFile as readFile3, writeFile as
|
|
174
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, chmod as chmod3 } from "fs/promises";
|
|
64
175
|
import { homedir as homedir3 } from "os";
|
|
65
176
|
import { join as join3 } from "path";
|
|
66
177
|
var CONFIG_DIR3 = join3(homedir3(), ".repowise");
|
|
@@ -113,9 +224,9 @@ async function getStoredCredentials() {
|
|
|
113
224
|
}
|
|
114
225
|
}
|
|
115
226
|
async function storeCredentials(credentials) {
|
|
116
|
-
await
|
|
117
|
-
await
|
|
118
|
-
await
|
|
227
|
+
await mkdir3(CONFIG_DIR3, { recursive: true, mode: 448 });
|
|
228
|
+
await writeFile3(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2));
|
|
229
|
+
await chmod3(CREDENTIALS_PATH, 384);
|
|
119
230
|
}
|
|
120
231
|
async function getValidCredentials() {
|
|
121
232
|
const creds = await getStoredCredentials();
|
|
@@ -146,7 +257,7 @@ var PollClient = class {
|
|
|
146
257
|
constructor(apiUrl) {
|
|
147
258
|
this.apiUrl = apiUrl;
|
|
148
259
|
}
|
|
149
|
-
async poll(repoIds, since) {
|
|
260
|
+
async poll(repoIds, since, options) {
|
|
150
261
|
const credentials = await getValidCredentials();
|
|
151
262
|
if (!credentials) {
|
|
152
263
|
throw new AuthError("Not logged in. Run `repowise login` first.");
|
|
@@ -155,6 +266,9 @@ var PollClient = class {
|
|
|
155
266
|
repoIds: repoIds.join(","),
|
|
156
267
|
since
|
|
157
268
|
});
|
|
269
|
+
if (options?.includeActiveRepos) {
|
|
270
|
+
params.set("includeActiveRepos", "true");
|
|
271
|
+
}
|
|
158
272
|
const controller = new AbortController();
|
|
159
273
|
const timeoutId = setTimeout(() => controller.abort(), POLL_TIMEOUT_MS);
|
|
160
274
|
try {
|
|
@@ -239,7 +353,7 @@ function notifyContextUpdated(repoId, fileCount) {
|
|
|
239
353
|
|
|
240
354
|
// ../listener/dist/context-fetcher.js
|
|
241
355
|
import { execFile } from "child_process";
|
|
242
|
-
import { mkdir as
|
|
356
|
+
import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
|
|
243
357
|
import { join as join5 } from "path";
|
|
244
358
|
import { promisify } from "util";
|
|
245
359
|
|
|
@@ -337,7 +451,7 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
|
|
|
337
451
|
return { success: true, updatedFiles: [] };
|
|
338
452
|
}
|
|
339
453
|
const contextDir = join5(localPath, "repowise-context");
|
|
340
|
-
await
|
|
454
|
+
await mkdir4(contextDir, { recursive: true });
|
|
341
455
|
const updatedFiles = [];
|
|
342
456
|
for (const file of files) {
|
|
343
457
|
const urlRes = await fetch(`${apiUrl}/v1/repos/${repoId}/context/${file.fileName}`, {
|
|
@@ -359,7 +473,7 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
|
|
|
359
473
|
continue;
|
|
360
474
|
}
|
|
361
475
|
const content = await contentRes.text();
|
|
362
|
-
await
|
|
476
|
+
await writeFile4(join5(contextDir, file.fileName), content, "utf-8");
|
|
363
477
|
updatedFiles.push(file.fileName);
|
|
364
478
|
}
|
|
365
479
|
console.log(`Context fetch for ${repoId}: downloaded ${updatedFiles.length}/${files.length} file(s)`);
|
|
@@ -377,8 +491,8 @@ var PID_PATH = join6(homedir4(), ".repowise", "listener.pid");
|
|
|
377
491
|
var running = false;
|
|
378
492
|
var sleepResolve = null;
|
|
379
493
|
async function writePidFile() {
|
|
380
|
-
await
|
|
381
|
-
await
|
|
494
|
+
await mkdir5(join6(homedir4(), ".repowise"), { recursive: true });
|
|
495
|
+
await writeFile5(PID_PATH, String(process.pid));
|
|
382
496
|
}
|
|
383
497
|
async function removePidFile() {
|
|
384
498
|
try {
|
|
@@ -541,6 +655,8 @@ async function startListener(options) {
|
|
|
541
655
|
}
|
|
542
656
|
}
|
|
543
657
|
let pollIntervalMs = 5e3;
|
|
658
|
+
let pollCycleCount = 0;
|
|
659
|
+
const RECONCILE_EVERY_N_CYCLES = 60;
|
|
544
660
|
const origLog = console.log.bind(console);
|
|
545
661
|
const origError = console.error.bind(console);
|
|
546
662
|
const origWarn = console.warn.bind(console);
|
|
@@ -566,6 +682,8 @@ async function startListener(options) {
|
|
|
566
682
|
let authErrorGroup = null;
|
|
567
683
|
let minPollInterval = pollIntervalMs;
|
|
568
684
|
let connectionLostNotified = false;
|
|
685
|
+
pollCycleCount++;
|
|
686
|
+
const shouldReconcile = pollCycleCount % RECONCILE_EVERY_N_CYCLES === 0;
|
|
569
687
|
for (const group of groups) {
|
|
570
688
|
if (!running)
|
|
571
689
|
break;
|
|
@@ -577,7 +695,31 @@ async function startListener(options) {
|
|
|
577
695
|
const ts2 = state.repos[id]?.lastSyncTimestamp ?? now;
|
|
578
696
|
return ts2 < earliest ? ts2 : earliest;
|
|
579
697
|
}, now);
|
|
580
|
-
const response = await group.pollClient.poll(group.repoIds, sinceTimestamp
|
|
698
|
+
const response = await group.pollClient.poll(group.repoIds, sinceTimestamp, {
|
|
699
|
+
includeActiveRepos: shouldReconcile
|
|
700
|
+
});
|
|
701
|
+
if (response.activeRepos && response.activeRepos.length > 0) {
|
|
702
|
+
try {
|
|
703
|
+
const result = reconcileRepos(config2.repos, response.activeRepos, state, group.apiUrl);
|
|
704
|
+
if (result.updated) {
|
|
705
|
+
config2.repos = result.repos;
|
|
706
|
+
await saveListenerConfig(config2);
|
|
707
|
+
await saveState(state);
|
|
708
|
+
group.repoIds = result.repos.filter((r) => (r.apiUrl ?? config2.defaultApiUrl) === group.apiUrl).map((r) => r.repoId);
|
|
709
|
+
group.repoLocalPaths.clear();
|
|
710
|
+
for (const r of result.repos) {
|
|
711
|
+
if ((r.apiUrl ?? config2.defaultApiUrl) === group.apiUrl) {
|
|
712
|
+
group.repoLocalPaths.set(r.repoId, r.localPath);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
for (const change of result.changes) {
|
|
716
|
+
console.log(`[reconcile] ${change}`);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
} catch (reconcileErr) {
|
|
720
|
+
console.warn("Repo reconciliation failed \u2014 will retry next cycle", reconcileErr instanceof Error ? reconcileErr.message : String(reconcileErr));
|
|
721
|
+
}
|
|
722
|
+
}
|
|
581
723
|
if (group.offline.isOffline) {
|
|
582
724
|
await handleCatchUp(group.offline, group.pollClient, group.repoIds, state, group.repoLocalPaths, group.apiUrl);
|
|
583
725
|
group.offline.isOffline = false;
|
|
@@ -684,7 +826,7 @@ function getEnvConfig() {
|
|
|
684
826
|
import chalk from "chalk";
|
|
685
827
|
|
|
686
828
|
// src/lib/config.ts
|
|
687
|
-
import { readFile as readFile5, writeFile as
|
|
829
|
+
import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
|
|
688
830
|
import { homedir as homedir5 } from "os";
|
|
689
831
|
import { join as join7 } from "path";
|
|
690
832
|
var CONFIG_DIR4 = join7(homedir5(), ".repowise");
|
|
@@ -698,8 +840,8 @@ async function getConfig() {
|
|
|
698
840
|
}
|
|
699
841
|
}
|
|
700
842
|
async function saveConfig(config2) {
|
|
701
|
-
await
|
|
702
|
-
await
|
|
843
|
+
await mkdir6(CONFIG_DIR4, { recursive: true });
|
|
844
|
+
await writeFile6(CONFIG_PATH2, JSON.stringify(config2, null, 2));
|
|
703
845
|
}
|
|
704
846
|
|
|
705
847
|
// src/lib/welcome.ts
|
|
@@ -743,7 +885,7 @@ import { join as join12 } from "path";
|
|
|
743
885
|
// ../listener/dist/process-manager.js
|
|
744
886
|
import { spawn } from "child_process";
|
|
745
887
|
import { openSync, closeSync } from "fs";
|
|
746
|
-
import { readFile as readFile6, writeFile as
|
|
888
|
+
import { readFile as readFile6, writeFile as writeFile7, mkdir as mkdir7, unlink as unlink2 } from "fs/promises";
|
|
747
889
|
import { homedir as homedir6 } from "os";
|
|
748
890
|
import { join as join8 } from "path";
|
|
749
891
|
import { createRequire } from "module";
|
|
@@ -781,7 +923,7 @@ function isAlive(pid) {
|
|
|
781
923
|
}
|
|
782
924
|
}
|
|
783
925
|
async function startBackground() {
|
|
784
|
-
await
|
|
926
|
+
await mkdir7(LOG_DIR, { recursive: true });
|
|
785
927
|
const cmd = resolveListenerCommand();
|
|
786
928
|
const stdoutFd = openSync(join8(LOG_DIR, "listener-stdout.log"), "a");
|
|
787
929
|
const stderrFd = openSync(join8(LOG_DIR, "listener-stderr.log"), "a");
|
|
@@ -797,7 +939,7 @@ async function startBackground() {
|
|
|
797
939
|
const pid = child.pid;
|
|
798
940
|
if (!pid)
|
|
799
941
|
throw new Error("Failed to spawn listener process");
|
|
800
|
-
await
|
|
942
|
+
await writeFile7(PID_PATH2, String(pid));
|
|
801
943
|
return pid;
|
|
802
944
|
}
|
|
803
945
|
async function stopProcess() {
|
|
@@ -846,7 +988,7 @@ async function removePidFile2() {
|
|
|
846
988
|
|
|
847
989
|
// ../listener/dist/service-installer.js
|
|
848
990
|
import { execFile as execFile2 } from "child_process";
|
|
849
|
-
import { writeFile as
|
|
991
|
+
import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink3 } from "fs/promises";
|
|
850
992
|
import { homedir as homedir7 } from "os";
|
|
851
993
|
import { join as join9 } from "path";
|
|
852
994
|
import { createRequire as createRequire2 } from "module";
|
|
@@ -908,13 +1050,13 @@ ${programArgs}
|
|
|
908
1050
|
</plist>`;
|
|
909
1051
|
}
|
|
910
1052
|
async function darwinInstall() {
|
|
911
|
-
await
|
|
912
|
-
await
|
|
1053
|
+
await mkdir8(logDir(), { recursive: true });
|
|
1054
|
+
await mkdir8(join9(homedir7(), "Library", "LaunchAgents"), { recursive: true });
|
|
913
1055
|
try {
|
|
914
1056
|
await exec("launchctl", ["unload", plistPath()]);
|
|
915
1057
|
} catch {
|
|
916
1058
|
}
|
|
917
|
-
await
|
|
1059
|
+
await writeFile8(plistPath(), buildPlist());
|
|
918
1060
|
await exec("launchctl", ["load", plistPath()]);
|
|
919
1061
|
}
|
|
920
1062
|
async function darwinUninstall() {
|
|
@@ -960,9 +1102,9 @@ StandardError=append:${join9(logs, "listener-stderr.log")}
|
|
|
960
1102
|
WantedBy=default.target`;
|
|
961
1103
|
}
|
|
962
1104
|
async function linuxInstall() {
|
|
963
|
-
await
|
|
964
|
-
await
|
|
965
|
-
await
|
|
1105
|
+
await mkdir8(logDir(), { recursive: true });
|
|
1106
|
+
await mkdir8(join9(homedir7(), ".config", "systemd", "user"), { recursive: true });
|
|
1107
|
+
await writeFile8(unitPath(), buildUnit());
|
|
966
1108
|
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
967
1109
|
await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
|
|
968
1110
|
await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
|
|
@@ -995,7 +1137,7 @@ async function linuxIsInstalled() {
|
|
|
995
1137
|
}
|
|
996
1138
|
var TASK_NAME = "RepoWise Listener";
|
|
997
1139
|
async function win32Install() {
|
|
998
|
-
await
|
|
1140
|
+
await mkdir8(logDir(), { recursive: true });
|
|
999
1141
|
const cmd = resolveListenerCommand2();
|
|
1000
1142
|
const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
|
|
1001
1143
|
await exec("schtasks", [
|
|
@@ -1079,7 +1221,7 @@ import ora from "ora";
|
|
|
1079
1221
|
|
|
1080
1222
|
// src/lib/auth.ts
|
|
1081
1223
|
import { createHash, randomBytes } from "crypto";
|
|
1082
|
-
import { readFile as readFile7, writeFile as
|
|
1224
|
+
import { readFile as readFile7, writeFile as writeFile9, mkdir as mkdir9, chmod as chmod4, unlink as unlink4 } from "fs/promises";
|
|
1083
1225
|
import http from "http";
|
|
1084
1226
|
import { homedir as homedir8 } from "os";
|
|
1085
1227
|
import { join as join10 } from "path";
|
|
@@ -1260,9 +1402,9 @@ async function getStoredCredentials2() {
|
|
|
1260
1402
|
}
|
|
1261
1403
|
}
|
|
1262
1404
|
async function storeCredentials2(credentials) {
|
|
1263
|
-
await
|
|
1264
|
-
await
|
|
1265
|
-
await
|
|
1405
|
+
await mkdir9(CONFIG_DIR5, { recursive: true, mode: 448 });
|
|
1406
|
+
await writeFile9(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2));
|
|
1407
|
+
await chmod4(CREDENTIALS_PATH2, 384);
|
|
1266
1408
|
}
|
|
1267
1409
|
async function clearCredentials() {
|
|
1268
1410
|
try {
|
|
@@ -1440,8 +1582,8 @@ async function selectAiTools() {
|
|
|
1440
1582
|
}
|
|
1441
1583
|
|
|
1442
1584
|
// src/lib/ai-tools.ts
|
|
1443
|
-
import { readFile as readFile8, writeFile as
|
|
1444
|
-
import { join as join11, dirname } from "path";
|
|
1585
|
+
import { readFile as readFile8, writeFile as writeFile10, mkdir as mkdir10, readdir } from "fs/promises";
|
|
1586
|
+
import { join as join11, dirname as dirname2 } from "path";
|
|
1445
1587
|
var AI_TOOL_CONFIG = {
|
|
1446
1588
|
cursor: {
|
|
1447
1589
|
label: "Cursor",
|
|
@@ -1567,9 +1709,9 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
|
|
|
1567
1709
|
async function updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
|
|
1568
1710
|
const config2 = AI_TOOL_CONFIG[tool];
|
|
1569
1711
|
const fullPath = join11(repoRoot, config2.filePath);
|
|
1570
|
-
const dir =
|
|
1712
|
+
const dir = dirname2(fullPath);
|
|
1571
1713
|
if (dir !== repoRoot) {
|
|
1572
|
-
await
|
|
1714
|
+
await mkdir10(dir, { recursive: true });
|
|
1573
1715
|
}
|
|
1574
1716
|
const referenceBlock = generateReference(tool, repoName, contextFolder, contextFiles);
|
|
1575
1717
|
let existing = "";
|
|
@@ -1591,7 +1733,7 @@ async function updateToolConfig(repoRoot, tool, repoName, contextFolder, context
|
|
|
1591
1733
|
const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
|
|
1592
1734
|
content = existing + separator + referenceBlock + "\n";
|
|
1593
1735
|
}
|
|
1594
|
-
await
|
|
1736
|
+
await writeFile10(fullPath, content, "utf-8");
|
|
1595
1737
|
return { created };
|
|
1596
1738
|
}
|
|
1597
1739
|
async function scanLocalContextFiles(repoRoot, contextFolder) {
|
|
@@ -2098,6 +2240,8 @@ async function create() {
|
|
|
2098
2240
|
let repoId;
|
|
2099
2241
|
let repoName;
|
|
2100
2242
|
let repoRoot;
|
|
2243
|
+
let repoPlatform;
|
|
2244
|
+
let repoExternalId;
|
|
2101
2245
|
spinner.start("Checking for pending repository...");
|
|
2102
2246
|
try {
|
|
2103
2247
|
const pending = await apiRequest("/v1/onboarding/pending");
|
|
@@ -2130,6 +2274,8 @@ async function create() {
|
|
|
2130
2274
|
const match = repos.find((r) => r.name === repoName || r.fullName.endsWith(`/${repoName}`));
|
|
2131
2275
|
if (match) {
|
|
2132
2276
|
repoId = match.repoId;
|
|
2277
|
+
repoPlatform = match.platform;
|
|
2278
|
+
repoExternalId = match.externalId;
|
|
2133
2279
|
}
|
|
2134
2280
|
} catch {
|
|
2135
2281
|
}
|
|
@@ -2148,6 +2294,26 @@ async function create() {
|
|
|
2148
2294
|
process.exitCode = 1;
|
|
2149
2295
|
return;
|
|
2150
2296
|
}
|
|
2297
|
+
try {
|
|
2298
|
+
const pricing = await apiRequest(
|
|
2299
|
+
`/v1/repos/${repoId}/rescan-pricing`
|
|
2300
|
+
);
|
|
2301
|
+
if (pricing.lastFullScanAt) {
|
|
2302
|
+
spinner.fail(chalk5.red("This repository already has context generated."));
|
|
2303
|
+
console.log(
|
|
2304
|
+
chalk5.cyan(
|
|
2305
|
+
`
|
|
2306
|
+
To trigger a new full scan, visit the dashboard:
|
|
2307
|
+
https://app.repowise.ai/repos/${repoId}
|
|
2308
|
+
`
|
|
2309
|
+
)
|
|
2310
|
+
);
|
|
2311
|
+
console.log(chalk5.dim(" To sync recent changes, use: repowise sync\n"));
|
|
2312
|
+
process.exitCode = 1;
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
} catch {
|
|
2316
|
+
}
|
|
2151
2317
|
const { tools, hasOther } = await selectAiTools();
|
|
2152
2318
|
if (hasOther) {
|
|
2153
2319
|
console.log(
|
|
@@ -2328,6 +2494,8 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
2328
2494
|
if (isStagingMode()) {
|
|
2329
2495
|
repoEntry.apiUrl = getEnvConfig().apiUrl;
|
|
2330
2496
|
}
|
|
2497
|
+
if (repoPlatform) repoEntry.platform = repoPlatform;
|
|
2498
|
+
if (repoExternalId) repoEntry.externalId = repoExternalId;
|
|
2331
2499
|
updatedRepos.push(repoEntry);
|
|
2332
2500
|
}
|
|
2333
2501
|
await saveConfig({
|
|
@@ -2445,7 +2613,7 @@ async function logout() {
|
|
|
2445
2613
|
// src/commands/status.ts
|
|
2446
2614
|
import { readFile as readFile9 } from "fs/promises";
|
|
2447
2615
|
import { homedir as homedir9 } from "os";
|
|
2448
|
-
import { basename, join as join13 } from "path";
|
|
2616
|
+
import { basename as basename2, join as join13 } from "path";
|
|
2449
2617
|
var STATE_PATH2 = join13(homedir9(), ".repowise", "listener-state.json");
|
|
2450
2618
|
var CONFIG_PATH3 = join13(homedir9(), ".repowise", "config.json");
|
|
2451
2619
|
async function status() {
|
|
@@ -2490,7 +2658,7 @@ async function status() {
|
|
|
2490
2658
|
const configData = await readFile9(CONFIG_PATH3, "utf-8");
|
|
2491
2659
|
const config2 = JSON.parse(configData);
|
|
2492
2660
|
for (const repo of config2.repos ?? []) {
|
|
2493
|
-
repoNames.set(repo.repoId,
|
|
2661
|
+
repoNames.set(repo.repoId, basename2(repo.localPath));
|
|
2494
2662
|
}
|
|
2495
2663
|
} catch {
|
|
2496
2664
|
}
|
|
@@ -2859,7 +3027,7 @@ async function config() {
|
|
|
2859
3027
|
|
|
2860
3028
|
// bin/repowise.ts
|
|
2861
3029
|
var __filename = fileURLToPath3(import.meta.url);
|
|
2862
|
-
var __dirname =
|
|
3030
|
+
var __dirname = dirname3(__filename);
|
|
2863
3031
|
var pkg = JSON.parse(readFileSync(join15(__dirname, "..", "..", "package.json"), "utf-8"));
|
|
2864
3032
|
var program = new Command();
|
|
2865
3033
|
program.name("repowise").description("AI-optimized codebase context generator").version(pkg.version).option("--staging", "Use the staging environment").hook("preAction", async () => {
|