revyse 0.1.0 → 0.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.
- package/README.md +7 -6
- package/dist/agent.js +11 -0
- package/dist/chunk-FYWZYXR4.js +24106 -0
- package/dist/headless.js +392 -129
- package/dist/index.js +212 -24043
- package/dist/overlay.js +130 -61
- package/package.json +5 -1
package/dist/headless.js
CHANGED
|
@@ -3116,9 +3116,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3116
3116
|
* @param {string} [path]
|
|
3117
3117
|
* @return {(string|null|Command)}
|
|
3118
3118
|
*/
|
|
3119
|
-
executableDir(
|
|
3120
|
-
if (
|
|
3121
|
-
this._executableDir =
|
|
3119
|
+
executableDir(path30) {
|
|
3120
|
+
if (path30 === void 0) return this._executableDir;
|
|
3121
|
+
this._executableDir = path30;
|
|
3122
3122
|
return this;
|
|
3123
3123
|
}
|
|
3124
3124
|
/**
|
|
@@ -3374,8 +3374,8 @@ function useColor() {
|
|
|
3374
3374
|
var program = new Command();
|
|
3375
3375
|
|
|
3376
3376
|
// src/commands/change.ts
|
|
3377
|
-
import { readFileSync as
|
|
3378
|
-
import
|
|
3377
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
3378
|
+
import path29 from "path";
|
|
3379
3379
|
|
|
3380
3380
|
// ../agent-core/src/path-policy.ts
|
|
3381
3381
|
import { realpathSync } from "fs";
|
|
@@ -4292,10 +4292,10 @@ function mergeDefs(...defs) {
|
|
|
4292
4292
|
function cloneDef(schema) {
|
|
4293
4293
|
return mergeDefs(schema._zod.def);
|
|
4294
4294
|
}
|
|
4295
|
-
function getElementAtPath(obj,
|
|
4296
|
-
if (!
|
|
4295
|
+
function getElementAtPath(obj, path30) {
|
|
4296
|
+
if (!path30)
|
|
4297
4297
|
return obj;
|
|
4298
|
-
return
|
|
4298
|
+
return path30.reduce((acc, key) => acc?.[key], obj);
|
|
4299
4299
|
}
|
|
4300
4300
|
function promiseAllObject(promisesObj) {
|
|
4301
4301
|
const keys = Object.keys(promisesObj);
|
|
@@ -4635,11 +4635,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
4635
4635
|
}
|
|
4636
4636
|
return false;
|
|
4637
4637
|
}
|
|
4638
|
-
function prefixIssues(
|
|
4638
|
+
function prefixIssues(path30, issues) {
|
|
4639
4639
|
return issues.map((iss) => {
|
|
4640
4640
|
var _a3;
|
|
4641
4641
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
4642
|
-
iss.path.unshift(
|
|
4642
|
+
iss.path.unshift(path30);
|
|
4643
4643
|
return iss;
|
|
4644
4644
|
});
|
|
4645
4645
|
}
|
|
@@ -5101,16 +5101,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5101
5101
|
}
|
|
5102
5102
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
5103
5103
|
const fieldErrors = { _errors: [] };
|
|
5104
|
-
const processError = (error63,
|
|
5104
|
+
const processError = (error63, path30 = []) => {
|
|
5105
5105
|
for (const issue2 of error63.issues) {
|
|
5106
5106
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5107
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5107
|
+
issue2.errors.map((issues) => processError({ issues }, [...path30, ...issue2.path]));
|
|
5108
5108
|
} else if (issue2.code === "invalid_key") {
|
|
5109
|
-
processError({ issues: issue2.issues }, [...
|
|
5109
|
+
processError({ issues: issue2.issues }, [...path30, ...issue2.path]);
|
|
5110
5110
|
} else if (issue2.code === "invalid_element") {
|
|
5111
|
-
processError({ issues: issue2.issues }, [...
|
|
5111
|
+
processError({ issues: issue2.issues }, [...path30, ...issue2.path]);
|
|
5112
5112
|
} else {
|
|
5113
|
-
const fullpath = [...
|
|
5113
|
+
const fullpath = [...path30, ...issue2.path];
|
|
5114
5114
|
if (fullpath.length === 0) {
|
|
5115
5115
|
fieldErrors._errors.push(mapper(issue2));
|
|
5116
5116
|
} else {
|
|
@@ -5149,17 +5149,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5149
5149
|
}
|
|
5150
5150
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
5151
5151
|
const result = { errors: [] };
|
|
5152
|
-
const processError = (error63,
|
|
5152
|
+
const processError = (error63, path30 = []) => {
|
|
5153
5153
|
var _a3;
|
|
5154
5154
|
for (const issue2 of error63.issues) {
|
|
5155
5155
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5156
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5156
|
+
issue2.errors.map((issues) => processError({ issues }, [...path30, ...issue2.path]));
|
|
5157
5157
|
} else if (issue2.code === "invalid_key") {
|
|
5158
|
-
processError({ issues: issue2.issues }, [...
|
|
5158
|
+
processError({ issues: issue2.issues }, [...path30, ...issue2.path]);
|
|
5159
5159
|
} else if (issue2.code === "invalid_element") {
|
|
5160
|
-
processError({ issues: issue2.issues }, [...
|
|
5160
|
+
processError({ issues: issue2.issues }, [...path30, ...issue2.path]);
|
|
5161
5161
|
} else {
|
|
5162
|
-
const fullpath = [...
|
|
5162
|
+
const fullpath = [...path30, ...issue2.path];
|
|
5163
5163
|
if (fullpath.length === 0) {
|
|
5164
5164
|
result.errors.push(mapper(issue2));
|
|
5165
5165
|
continue;
|
|
@@ -5198,8 +5198,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5198
5198
|
}
|
|
5199
5199
|
function toDotPath(_path) {
|
|
5200
5200
|
const segs = [];
|
|
5201
|
-
const
|
|
5202
|
-
for (const seg of
|
|
5201
|
+
const path30 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
5202
|
+
for (const seg of path30) {
|
|
5203
5203
|
if (typeof seg === "number")
|
|
5204
5204
|
segs.push(`[${seg}]`);
|
|
5205
5205
|
else if (typeof seg === "symbol")
|
|
@@ -6301,9 +6301,9 @@ var asciiTabOrNewline = /[\t\n\r]/g;
|
|
|
6301
6301
|
function stripTabAndNewline(value) {
|
|
6302
6302
|
return value.replace(asciiTabOrNewline, "");
|
|
6303
6303
|
}
|
|
6304
|
-
function urlHostnameOk(url2,
|
|
6305
|
-
|
|
6306
|
-
return
|
|
6304
|
+
function urlHostnameOk(url2, hostname4) {
|
|
6305
|
+
hostname4.lastIndex = 0;
|
|
6306
|
+
return hostname4.test(url2.hostname);
|
|
6307
6307
|
}
|
|
6308
6308
|
function urlProtocolOk(url2, protocol) {
|
|
6309
6309
|
protocol.lastIndex = 0;
|
|
@@ -22337,13 +22337,13 @@ function resolveRef(ref, ctx) {
|
|
|
22337
22337
|
if (!ref.startsWith("#")) {
|
|
22338
22338
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
22339
22339
|
}
|
|
22340
|
-
const
|
|
22341
|
-
if (
|
|
22340
|
+
const path30 = ref.slice(1).split("/").filter(Boolean);
|
|
22341
|
+
if (path30.length === 0) {
|
|
22342
22342
|
return ctx.rootSchema;
|
|
22343
22343
|
}
|
|
22344
22344
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
22345
|
-
if (
|
|
22346
|
-
const key =
|
|
22345
|
+
if (path30[0] === defsKey) {
|
|
22346
|
+
const key = path30[1] === void 0 ? void 0 : decodeJSONPointerSegment(path30[1]);
|
|
22347
22347
|
if (!key || !ctx.defs[key]) {
|
|
22348
22348
|
throw new Error(`Reference not found: ${ref}`);
|
|
22349
22349
|
}
|
|
@@ -24641,16 +24641,16 @@ var Diff = class {
|
|
|
24641
24641
|
}
|
|
24642
24642
|
}
|
|
24643
24643
|
}
|
|
24644
|
-
addToPath(
|
|
24645
|
-
const last =
|
|
24644
|
+
addToPath(path30, added, removed, oldPosInc, options) {
|
|
24645
|
+
const last = path30.lastComponent;
|
|
24646
24646
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
24647
24647
|
return {
|
|
24648
|
-
oldPos:
|
|
24648
|
+
oldPos: path30.oldPos + oldPosInc,
|
|
24649
24649
|
lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
|
|
24650
24650
|
};
|
|
24651
24651
|
} else {
|
|
24652
24652
|
return {
|
|
24653
|
-
oldPos:
|
|
24653
|
+
oldPos: path30.oldPos + oldPosInc,
|
|
24654
24654
|
lastComponent: { count: 1, added, removed, previousComponent: last }
|
|
24655
24655
|
};
|
|
24656
24656
|
}
|
|
@@ -30455,17 +30455,17 @@ var writeToFiles = (chunks, stdioItems, outputFiles) => {
|
|
|
30455
30455
|
const writtenPaths = /* @__PURE__ */ new Set();
|
|
30456
30456
|
for (const stdioItem of fileItems) {
|
|
30457
30457
|
const { type, append } = stdioItem;
|
|
30458
|
-
const
|
|
30459
|
-
if (writtenPaths.has(
|
|
30458
|
+
const path30 = type === "fileNumber" ? stdioItem.path : getFileTarget(stdioItem);
|
|
30459
|
+
if (writtenPaths.has(path30)) {
|
|
30460
30460
|
continue;
|
|
30461
30461
|
}
|
|
30462
|
-
writtenPaths.add(
|
|
30463
|
-
if (append || outputFiles.has(
|
|
30464
|
-
appendFileSync(
|
|
30462
|
+
writtenPaths.add(path30);
|
|
30463
|
+
if (append || outputFiles.has(path30)) {
|
|
30464
|
+
appendFileSync(path30, contents);
|
|
30465
30465
|
} else {
|
|
30466
|
-
writeFileSync2(
|
|
30466
|
+
writeFileSync2(path30, contents);
|
|
30467
30467
|
}
|
|
30468
|
-
outputFiles.add(
|
|
30468
|
+
outputFiles.add(path30);
|
|
30469
30469
|
}
|
|
30470
30470
|
};
|
|
30471
30471
|
|
|
@@ -33611,7 +33611,7 @@ function createAgentApi(opts) {
|
|
|
33611
33611
|
const f = opts.fetch ?? fetch;
|
|
33612
33612
|
const base = opts.apiUrl.replace(/\/+$/, "");
|
|
33613
33613
|
const auth = { authorization: `Bearer ${opts.token}` };
|
|
33614
|
-
const call = (
|
|
33614
|
+
const call = (path30, body, signal) => f(`${base}${path30}`, {
|
|
33615
33615
|
method: "POST",
|
|
33616
33616
|
headers: { "content-type": "application/json", ...auth },
|
|
33617
33617
|
body: JSON.stringify(body),
|
|
@@ -33627,18 +33627,18 @@ function createAgentApi(opts) {
|
|
|
33627
33627
|
}
|
|
33628
33628
|
return {
|
|
33629
33629
|
baseUrl: base,
|
|
33630
|
-
async get(
|
|
33631
|
-
const res = await f(`${base}${
|
|
33630
|
+
async get(path30) {
|
|
33631
|
+
const res = await f(`${base}${path30}`, { headers: auth });
|
|
33632
33632
|
if (!res.ok) throw await failure2(res);
|
|
33633
33633
|
return await res.json();
|
|
33634
33634
|
},
|
|
33635
|
-
async post(
|
|
33636
|
-
const res = await call(
|
|
33635
|
+
async post(path30, body) {
|
|
33636
|
+
const res = await call(path30, body);
|
|
33637
33637
|
if (!res.ok) throw await failure2(res);
|
|
33638
33638
|
return await res.json();
|
|
33639
33639
|
},
|
|
33640
|
-
async *stream(
|
|
33641
|
-
const res = await call(
|
|
33640
|
+
async *stream(path30, body, signal) {
|
|
33641
|
+
const res = await call(path30, body, signal);
|
|
33642
33642
|
if (!res.ok) throw await failure2(res);
|
|
33643
33643
|
if (!res.body) return;
|
|
33644
33644
|
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
@@ -33823,27 +33823,27 @@ var StagedFiles = class {
|
|
|
33823
33823
|
/** Paths written by the package manager (I7), not by the model: the write rules don't apply. */
|
|
33824
33824
|
fromPackageManager = /* @__PURE__ */ new Set();
|
|
33825
33825
|
/** Current content as the agent sees it: staged if written, else the project's. */
|
|
33826
|
-
async read(
|
|
33827
|
-
const s = this.staged.get(
|
|
33826
|
+
async read(path30) {
|
|
33827
|
+
const s = this.staged.get(path30);
|
|
33828
33828
|
if (s) return s.content;
|
|
33829
|
-
const content = await this.host.read(
|
|
33830
|
-
if (!this.seen.has(
|
|
33829
|
+
const content = await this.host.read(path30);
|
|
33830
|
+
if (!this.seen.has(path30)) this.seen.set(path30, content === null ? null : hashContent(content));
|
|
33831
33831
|
return content;
|
|
33832
33832
|
}
|
|
33833
33833
|
/** Like `read`, without recording a snapshot: for searching, which only looks. */
|
|
33834
|
-
async peek(
|
|
33835
|
-
const s = this.staged.get(
|
|
33836
|
-
return s ? s.content : this.host.read(
|
|
33834
|
+
async peek(path30) {
|
|
33835
|
+
const s = this.staged.get(path30);
|
|
33836
|
+
return s ? s.content : this.host.read(path30);
|
|
33837
33837
|
}
|
|
33838
33838
|
/** Writes `content` (or deletes, with null) in the staging layer. */
|
|
33839
|
-
async write(
|
|
33840
|
-
const existing = this.staged.get(
|
|
33839
|
+
async write(path30, content) {
|
|
33840
|
+
const existing = this.staged.get(path30);
|
|
33841
33841
|
if (existing) {
|
|
33842
33842
|
existing.content = content;
|
|
33843
33843
|
return;
|
|
33844
33844
|
}
|
|
33845
|
-
const before = await this.read(
|
|
33846
|
-
this.staged.set(
|
|
33845
|
+
const before = await this.read(path30);
|
|
33846
|
+
this.staged.set(path30, {
|
|
33847
33847
|
content,
|
|
33848
33848
|
existed: before !== null,
|
|
33849
33849
|
baseHash: before === null ? null : hashContent(before)
|
|
@@ -33854,9 +33854,9 @@ var StagedFiles = class {
|
|
|
33854
33854
|
* a supported SDK). The patch engine applies it with the rest, so it lands, is checked and is
|
|
33855
33855
|
* reverted together; the write rules that keep the model out of these files don't apply.
|
|
33856
33856
|
*/
|
|
33857
|
-
async writeFromPackageManager(
|
|
33858
|
-
await this.write(
|
|
33859
|
-
this.fromPackageManager.add(
|
|
33857
|
+
async writeFromPackageManager(path30, content) {
|
|
33858
|
+
await this.write(path30, content);
|
|
33859
|
+
this.fromPackageManager.add(path30);
|
|
33860
33860
|
}
|
|
33861
33861
|
/** The paths the package manager wrote. */
|
|
33862
33862
|
packageManagerPaths() {
|
|
@@ -33874,18 +33874,18 @@ var StagedFiles = class {
|
|
|
33874
33874
|
/** What the apply phase changed, net: a file created then deleted is no change at all. */
|
|
33875
33875
|
changes() {
|
|
33876
33876
|
const out = [];
|
|
33877
|
-
for (const [
|
|
33877
|
+
for (const [path30, s] of [...this.staged].sort(([a2], [b]) => a2.localeCompare(b))) {
|
|
33878
33878
|
if (!s.existed && s.content === null) continue;
|
|
33879
|
-
if (!s.existed) out.push({ path:
|
|
33880
|
-
else if (s.content === null) out.push({ path:
|
|
33879
|
+
if (!s.existed) out.push({ path: path30, kind: "create", content: s.content });
|
|
33880
|
+
else if (s.content === null) out.push({ path: path30, kind: "delete", baseHash: s.baseHash });
|
|
33881
33881
|
else if (hashContent(s.content) !== s.baseHash)
|
|
33882
|
-
out.push({ path:
|
|
33882
|
+
out.push({ path: path30, kind: "modify", content: s.content, baseHash: s.baseHash });
|
|
33883
33883
|
}
|
|
33884
33884
|
return out;
|
|
33885
33885
|
}
|
|
33886
33886
|
/** Every file the agent read or wrote, with its hash when first seen (null: didn't exist). */
|
|
33887
33887
|
snapshots() {
|
|
33888
|
-
return [...this.seen].map(([
|
|
33888
|
+
return [...this.seen].map(([path30, hash2]) => ({ path: path30, hash: hash2 })).sort((a2, b) => a2.path.localeCompare(b.path));
|
|
33889
33889
|
}
|
|
33890
33890
|
};
|
|
33891
33891
|
|
|
@@ -33922,7 +33922,7 @@ var ok = (callId, output2) => ({
|
|
|
33922
33922
|
output: output2,
|
|
33923
33923
|
isError: false
|
|
33924
33924
|
});
|
|
33925
|
-
var secretFile = (
|
|
33925
|
+
var secretFile = (path30) => `Can't change ${path30}: it looks like it contains a secret. Leave it, and name it in notDone so the developer moves the secret to an environment variable.`;
|
|
33926
33926
|
var fail = (callId, output2) => ({
|
|
33927
33927
|
callId,
|
|
33928
33928
|
output: output2,
|
|
@@ -33965,12 +33965,12 @@ function createToolRuntime(opts) {
|
|
|
33965
33965
|
const found = [];
|
|
33966
33966
|
let withheld = 0;
|
|
33967
33967
|
let stopped = false;
|
|
33968
|
-
for (const
|
|
33968
|
+
for (const path30 of await visible(input2.path, input2.glob)) {
|
|
33969
33969
|
if (found.length >= max || Date.now() - started > budget) {
|
|
33970
33970
|
stopped = true;
|
|
33971
33971
|
break;
|
|
33972
33972
|
}
|
|
33973
|
-
const content = await staging.peek(
|
|
33973
|
+
const content = await staging.peek(path30);
|
|
33974
33974
|
if (content === null) continue;
|
|
33975
33975
|
if (containsSecret(content)) {
|
|
33976
33976
|
withheld++;
|
|
@@ -33982,7 +33982,7 @@ function createToolRuntime(opts) {
|
|
|
33982
33982
|
if (line.length > MAX_SEARCH_LINE || !matches(line)) continue;
|
|
33983
33983
|
const text = line.trim();
|
|
33984
33984
|
found.push(
|
|
33985
|
-
`${
|
|
33985
|
+
`${path30}:${i2 + 1}: ${text.length > MAX_LINE ? `${text.slice(0, MAX_LINE)}\u2026` : text}`
|
|
33986
33986
|
);
|
|
33987
33987
|
}
|
|
33988
33988
|
}
|
|
@@ -34183,7 +34183,7 @@ function createChangeRunner(opts) {
|
|
|
34183
34183
|
log(`change ${changeId ?? "(new)"} failed: ${err.message}`);
|
|
34184
34184
|
fail2(session, changeId, {
|
|
34185
34185
|
code: "internal",
|
|
34186
|
-
message: "Something went wrong in the Revyse agent. Check
|
|
34186
|
+
message: "Something went wrong in the Revyse agent. Check your dev server's output.",
|
|
34187
34187
|
retryable: true
|
|
34188
34188
|
});
|
|
34189
34189
|
}
|
|
@@ -34201,7 +34201,7 @@ function createChangeRunner(opts) {
|
|
|
34201
34201
|
}
|
|
34202
34202
|
function sessionOf(changeId) {
|
|
34203
34203
|
const s = sessions.get(changeId);
|
|
34204
|
-
if (!s) throw new Error("This change isn't open in this
|
|
34204
|
+
if (!s) throw new Error("This change isn't open in this session of your dev server");
|
|
34205
34205
|
return s;
|
|
34206
34206
|
}
|
|
34207
34207
|
function readFiles(paths) {
|
|
@@ -34302,7 +34302,7 @@ function createChangeRunner(opts) {
|
|
|
34302
34302
|
`/api/changes/${session.id}/plan`,
|
|
34303
34303
|
{
|
|
34304
34304
|
element: session.element,
|
|
34305
|
-
files: gathered.files.map(({ path:
|
|
34305
|
+
files: gathered.files.map(({ path: path30, content, hash: hash2 }) => ({ path: path30, content, hash: hash2 })),
|
|
34306
34306
|
modelTier: session.tier,
|
|
34307
34307
|
model: session.model,
|
|
34308
34308
|
...extra
|
|
@@ -34396,11 +34396,11 @@ function createChangeRunner(opts) {
|
|
|
34396
34396
|
if (!patch) return;
|
|
34397
34397
|
const [browser, baseline] = await Promise.all([browserPrep, baselinePrep]);
|
|
34398
34398
|
session.stage = "applying";
|
|
34399
|
-
const snapshots = files.map(({ path:
|
|
34399
|
+
const snapshots = files.map(({ path: path30, hash: hash2 }) => ({ path: path30, hash: hash2 }));
|
|
34400
34400
|
const applied = applyPatch({
|
|
34401
34401
|
projectRoot: opts.projectRoot,
|
|
34402
34402
|
changeId: session.id,
|
|
34403
|
-
patch: { files: patch.files.map(({ path:
|
|
34403
|
+
patch: { files: patch.files.map(({ path: path30, edits }) => ({ path: path30, edits })) },
|
|
34404
34404
|
snapshots
|
|
34405
34405
|
});
|
|
34406
34406
|
if (applied.status === "conflict") {
|
|
@@ -34978,13 +34978,113 @@ ${r.output.slice(-3e3)}`,
|
|
|
34978
34978
|
};
|
|
34979
34979
|
}
|
|
34980
34980
|
|
|
34981
|
+
// src/api-client.ts
|
|
34982
|
+
var DEFAULT_API_URL = "https://revyse.dev";
|
|
34983
|
+
function resolveApiUrl(flag, env = process.env) {
|
|
34984
|
+
return (flag || env.REVYSE_API_URL || DEFAULT_API_URL).replace(/\/+$/, "");
|
|
34985
|
+
}
|
|
34986
|
+
var ApiError = class extends Error {
|
|
34987
|
+
constructor(status, code2, message) {
|
|
34988
|
+
super(message);
|
|
34989
|
+
this.status = status;
|
|
34990
|
+
this.code = code2;
|
|
34991
|
+
this.name = "ApiError";
|
|
34992
|
+
}
|
|
34993
|
+
status;
|
|
34994
|
+
code;
|
|
34995
|
+
};
|
|
34996
|
+
function createApiClient({ apiUrl, token, fetch: f = fetch }) {
|
|
34997
|
+
async function request(method, path30, body) {
|
|
34998
|
+
const res = await f(`${apiUrl}${path30}`, {
|
|
34999
|
+
method,
|
|
35000
|
+
headers: {
|
|
35001
|
+
"content-type": "application/json",
|
|
35002
|
+
...token ? { authorization: `Bearer ${token}` } : {}
|
|
35003
|
+
},
|
|
35004
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
35005
|
+
});
|
|
35006
|
+
const data = await res.json().catch(() => ({}));
|
|
35007
|
+
if (!res.ok) {
|
|
35008
|
+
const code2 = data.error?.code ?? "error";
|
|
35009
|
+
throw new ApiError(res.status, code2, data.error?.message ?? `Request failed (${res.status})`);
|
|
35010
|
+
}
|
|
35011
|
+
return data;
|
|
35012
|
+
}
|
|
35013
|
+
return {
|
|
35014
|
+
get: (path30) => request("GET", path30),
|
|
35015
|
+
post: (path30, body) => request("POST", path30, body ?? {})
|
|
35016
|
+
};
|
|
35017
|
+
}
|
|
35018
|
+
|
|
35019
|
+
// src/connect.ts
|
|
35020
|
+
import { spawn as spawn2 } from "child_process";
|
|
35021
|
+
import { hostname as hostname3 } from "os";
|
|
35022
|
+
|
|
35023
|
+
// src/credentials.ts
|
|
35024
|
+
import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
35025
|
+
import { homedir } from "os";
|
|
35026
|
+
import path25 from "path";
|
|
35027
|
+
function credentialsPath(env = process.env) {
|
|
35028
|
+
const base = env.XDG_CONFIG_HOME || path25.join(homedir(), ".config");
|
|
35029
|
+
return path25.join(base, "revyse", "credentials.json");
|
|
35030
|
+
}
|
|
35031
|
+
function readAll(file2) {
|
|
35032
|
+
if (!existsSync4(file2)) return {};
|
|
35033
|
+
return JSON.parse(readFileSync9(file2, "utf8"));
|
|
35034
|
+
}
|
|
35035
|
+
function writeAll(file2, data) {
|
|
35036
|
+
mkdirSync3(path25.dirname(file2), { recursive: true, mode: 448 });
|
|
35037
|
+
writeFileSync5(file2, `${JSON.stringify(data, null, 2)}
|
|
35038
|
+
`, { mode: 384 });
|
|
35039
|
+
chmodSync(file2, 384);
|
|
35040
|
+
}
|
|
35041
|
+
function loadCredential(projectRoot, file2 = credentialsPath()) {
|
|
35042
|
+
return readAll(file2)[path25.resolve(projectRoot)] ?? null;
|
|
35043
|
+
}
|
|
35044
|
+
function saveCredential(projectRoot, cred, file2 = credentialsPath()) {
|
|
35045
|
+
writeAll(file2, { ...readAll(file2), [path25.resolve(projectRoot)]: cred });
|
|
35046
|
+
}
|
|
35047
|
+
|
|
35048
|
+
// src/device-login.ts
|
|
35049
|
+
var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
35050
|
+
async function deviceLogin({
|
|
35051
|
+
client,
|
|
35052
|
+
clientName,
|
|
35053
|
+
onCode,
|
|
35054
|
+
sleep = defaultSleep
|
|
35055
|
+
}) {
|
|
35056
|
+
const start = await client.post("/api/cli/device", { clientName });
|
|
35057
|
+
onCode(start);
|
|
35058
|
+
let interval = start.interval;
|
|
35059
|
+
const deadline = Date.now() + start.expiresIn * 1e3;
|
|
35060
|
+
while (Date.now() < deadline) {
|
|
35061
|
+
await sleep(interval * 1e3);
|
|
35062
|
+
try {
|
|
35063
|
+
return await client.post("/api/cli/token", { deviceCode: start.deviceCode });
|
|
35064
|
+
} catch (err) {
|
|
35065
|
+
if (!(err instanceof ApiError)) throw err;
|
|
35066
|
+
if (err.code === "authorization_pending") continue;
|
|
35067
|
+
if (err.code === "slow_down") {
|
|
35068
|
+
interval += 5;
|
|
35069
|
+
continue;
|
|
35070
|
+
}
|
|
35071
|
+
if (err.code === "access_denied") {
|
|
35072
|
+
throw new Error("Login was denied in the browser.", { cause: err });
|
|
35073
|
+
}
|
|
35074
|
+
if (err.code === "expired_token") break;
|
|
35075
|
+
throw err;
|
|
35076
|
+
}
|
|
35077
|
+
}
|
|
35078
|
+
throw new Error("Login code expired. Run `revyse login` again.");
|
|
35079
|
+
}
|
|
35080
|
+
|
|
34981
35081
|
// src/init-flow.ts
|
|
34982
|
-
import { existsSync as
|
|
34983
|
-
import
|
|
35082
|
+
import { existsSync as existsSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
|
|
35083
|
+
import path28 from "path";
|
|
34984
35084
|
|
|
34985
35085
|
// src/detect.ts
|
|
34986
|
-
import { existsSync as
|
|
34987
|
-
import
|
|
35086
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync6 } from "fs";
|
|
35087
|
+
import path26 from "path";
|
|
34988
35088
|
var LOCKFILES2 = [
|
|
34989
35089
|
["pnpm-lock.yaml", "pnpm"],
|
|
34990
35090
|
["yarn.lock", "yarn"],
|
|
@@ -34994,11 +35094,11 @@ var LOCKFILES2 = [
|
|
|
34994
35094
|
];
|
|
34995
35095
|
var SOURCE_DIR_CANDIDATES = ["src", "app", "pages", "components", "lib"];
|
|
34996
35096
|
function detectProject(root) {
|
|
34997
|
-
const pkgPath =
|
|
34998
|
-
if (!
|
|
35097
|
+
const pkgPath = path26.join(root, "package.json");
|
|
35098
|
+
if (!existsSync5(pkgPath)) {
|
|
34999
35099
|
throw new Error(`No package.json found in ${root}. Run revyse init from your project root.`);
|
|
35000
35100
|
}
|
|
35001
|
-
const pkg = JSON.parse(
|
|
35101
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
|
|
35002
35102
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
35003
35103
|
const warnings = [];
|
|
35004
35104
|
const framework = detectFramework(deps);
|
|
@@ -35007,13 +35107,13 @@ function detectProject(root) {
|
|
|
35007
35107
|
} else if (framework !== "nextjs") {
|
|
35008
35108
|
warnings.push(`${framework} support is experimental; Next.js is the supported target.`);
|
|
35009
35109
|
}
|
|
35010
|
-
const managers = LOCKFILES2.filter(([file2]) =>
|
|
35110
|
+
const managers = LOCKFILES2.filter(([file2]) => existsSync5(path26.join(root, file2)));
|
|
35011
35111
|
const packageManager = managers[0]?.[1] ?? "npm";
|
|
35012
35112
|
if (managers.length === 0) warnings.push("No lockfile found; defaulting to npm.");
|
|
35013
35113
|
if (new Set(managers.map(([, m]) => m)).size > 1) {
|
|
35014
35114
|
warnings.push(`Multiple lockfiles found; using ${packageManager}.`);
|
|
35015
35115
|
}
|
|
35016
|
-
const sourceDirs = SOURCE_DIR_CANDIDATES.filter((d) =>
|
|
35116
|
+
const sourceDirs = SOURCE_DIR_CANDIDATES.filter((d) => existsSync5(path26.join(root, d)));
|
|
35017
35117
|
for (const d of aliasedDirs(root)) if (!sourceDirs.includes(d)) sourceDirs.push(d);
|
|
35018
35118
|
if (sourceDirs.length === 0) {
|
|
35019
35119
|
warnings.push("No source directories found; edit sourceDirs in revyse.config.json.");
|
|
@@ -35086,17 +35186,17 @@ function hasSource(dir, depth = 3) {
|
|
|
35086
35186
|
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
35087
35187
|
if (entry.isFile() && SOURCE_FILE.test(entry.name)) return true;
|
|
35088
35188
|
if (entry.isDirectory() && depth > 1 && !NOT_SOURCE.has(entry.name)) {
|
|
35089
|
-
if (hasSource(
|
|
35189
|
+
if (hasSource(path26.join(dir, entry.name), depth - 1)) return true;
|
|
35090
35190
|
}
|
|
35091
35191
|
}
|
|
35092
35192
|
return false;
|
|
35093
35193
|
}
|
|
35094
35194
|
function aliasedDirs(root) {
|
|
35095
|
-
const file2 = ["tsconfig.json", "jsconfig.json"].map((f) =>
|
|
35195
|
+
const file2 = ["tsconfig.json", "jsconfig.json"].map((f) => path26.join(root, f)).find((f) => existsSync5(f));
|
|
35096
35196
|
if (!file2) return [];
|
|
35097
35197
|
let compilerOptions;
|
|
35098
35198
|
try {
|
|
35099
|
-
compilerOptions = parseJsonc(
|
|
35199
|
+
compilerOptions = parseJsonc(readFileSync10(file2, "utf8")).compilerOptions;
|
|
35100
35200
|
} catch {
|
|
35101
35201
|
return [];
|
|
35102
35202
|
}
|
|
@@ -35104,15 +35204,15 @@ function aliasedDirs(root) {
|
|
|
35104
35204
|
const baseUrl = compilerOptions?.baseUrl ?? ".";
|
|
35105
35205
|
const dirs = /* @__PURE__ */ new Set();
|
|
35106
35206
|
for (const target of Object.values(paths).flat()) {
|
|
35107
|
-
const rel =
|
|
35207
|
+
const rel = path26.relative(root, path26.join(root, baseUrl, target)).split(path26.sep).join("/");
|
|
35108
35208
|
const top = rel.split("/")[0];
|
|
35109
35209
|
if (rel.startsWith("..") || NOT_SOURCE.has(top)) continue;
|
|
35110
35210
|
if (top === "*" || top === "") {
|
|
35111
35211
|
for (const entry of readdirSync4(root, { withFileTypes: true }))
|
|
35112
35212
|
if (entry.isDirectory() && !NOT_SOURCE.has(entry.name) && !entry.name.startsWith(".")) {
|
|
35113
|
-
if (hasSource(
|
|
35213
|
+
if (hasSource(path26.join(root, entry.name))) dirs.add(entry.name);
|
|
35114
35214
|
}
|
|
35115
|
-
} else if (
|
|
35215
|
+
} else if (existsSync5(path26.join(root, top)) && statSync6(path26.join(root, top)).isDirectory()) {
|
|
35116
35216
|
dirs.add(top);
|
|
35117
35217
|
}
|
|
35118
35218
|
}
|
|
@@ -35154,8 +35254,8 @@ Full policy: ${DATA_HANDLING_URL}
|
|
|
35154
35254
|
`;
|
|
35155
35255
|
|
|
35156
35256
|
// src/next-config.ts
|
|
35157
|
-
import { existsSync as
|
|
35158
|
-
import
|
|
35257
|
+
import { existsSync as existsSync6, readFileSync as readFileSync11 } from "fs";
|
|
35258
|
+
import path27 from "path";
|
|
35159
35259
|
|
|
35160
35260
|
// ../../node_modules/.pnpm/@babel+parser@8.0.7/node_modules/@babel/parser/lib/index.js
|
|
35161
35261
|
var Position = class {
|
|
@@ -50913,7 +51013,7 @@ var NEXT_CONFIG_FILES = [
|
|
|
50913
51013
|
];
|
|
50914
51014
|
function findNextConfig(root) {
|
|
50915
51015
|
for (const name of NEXT_CONFIG_FILES) {
|
|
50916
|
-
if (
|
|
51016
|
+
if (existsSync6(path27.join(root, name))) return name;
|
|
50917
51017
|
}
|
|
50918
51018
|
return null;
|
|
50919
51019
|
}
|
|
@@ -50968,20 +51068,72 @@ ${importLine}`);
|
|
|
50968
51068
|
return { status: "unsupported", reason: "it has no `export default` or `module.exports`" };
|
|
50969
51069
|
}
|
|
50970
51070
|
function readNextConfig(root, file2) {
|
|
50971
|
-
return
|
|
51071
|
+
return readFileSync11(path27.join(root, file2), "utf8");
|
|
50972
51072
|
}
|
|
50973
51073
|
|
|
51074
|
+
// src/version.ts
|
|
51075
|
+
import { createRequire } from "module";
|
|
51076
|
+
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
51077
|
+
|
|
50974
51078
|
// src/init-flow.ts
|
|
50975
51079
|
var CONFIG_FILE = "revyse.config.json";
|
|
50976
51080
|
var GITIGNORE_ENTRY = ".revyse/";
|
|
51081
|
+
var PACKAGES = ["revyse", "@revyse/next"];
|
|
51082
|
+
var INSTALL = {
|
|
51083
|
+
npm: ["npm", "install", "--save-dev", "--no-audit", "--no-fund"],
|
|
51084
|
+
pnpm: ["pnpm", "add", "--save-dev"],
|
|
51085
|
+
yarn: ["yarn", "add", "--dev"],
|
|
51086
|
+
bun: ["bun", "add", "--dev"]
|
|
51087
|
+
};
|
|
51088
|
+
var installCommand = (pm, specs) => [
|
|
51089
|
+
...INSTALL[pm],
|
|
51090
|
+
...specs
|
|
51091
|
+
];
|
|
51092
|
+
var runScript = (pm, script) => pm === "npm" ? `npm run ${script}` : `${pm} ${script}`;
|
|
50977
51093
|
async function runInit(opts, io) {
|
|
50978
51094
|
const { root } = opts;
|
|
50979
|
-
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
50980
51095
|
const ask = (q) => opts.yes ? Promise.resolve(true) : io.confirm(q);
|
|
50981
|
-
|
|
50982
|
-
|
|
50983
|
-
|
|
51096
|
+
let configured;
|
|
51097
|
+
let status;
|
|
51098
|
+
if (existsSync7(path28.join(root, CONFIG_FILE)) && !opts.force) {
|
|
51099
|
+
const config3 = projectConfigSchema.parse(
|
|
51100
|
+
JSON.parse(readFileSync12(path28.join(root, CONFIG_FILE), "utf8"))
|
|
51101
|
+
);
|
|
51102
|
+
io.log(`${CONFIG_FILE} is already here; checking the rest of the setup.`);
|
|
51103
|
+
if (!config3.dataDisclosure || config3.dataDisclosure.version < DISCLOSURE_VERSION)
|
|
51104
|
+
io.warn(
|
|
51105
|
+
"How Revyse handles your code has changed: run `npx revyse init --force` to review it."
|
|
51106
|
+
);
|
|
51107
|
+
configured = { config: config3, nextConfigEdited: false };
|
|
51108
|
+
status = "exists";
|
|
51109
|
+
} else {
|
|
51110
|
+
configured = await configure(opts, io, ask);
|
|
51111
|
+
if (!configured) return { status: "declined" };
|
|
51112
|
+
status = "initialized";
|
|
51113
|
+
}
|
|
51114
|
+
const { config: config2 } = configured;
|
|
51115
|
+
const nextjs = config2.framework === "nextjs";
|
|
51116
|
+
let installed = !nextjs;
|
|
51117
|
+
if (nextjs && opts.setup) installed = await installPackages(root, config2, opts.setup, io, ask);
|
|
51118
|
+
let connection = null;
|
|
51119
|
+
if (opts.setup && opts.connect !== false)
|
|
51120
|
+
connection = await connectProject(root, config2, opts.setup, io, ask);
|
|
51121
|
+
if (nextjs) {
|
|
51122
|
+
const steps = [
|
|
51123
|
+
...installed ? [] : [`install ${PACKAGES.join(" and ")} as dev dependencies`],
|
|
51124
|
+
...connection ? [] : ["connect it to Revyse with `npx revyse login`"]
|
|
51125
|
+
];
|
|
51126
|
+
io.log(
|
|
51127
|
+
steps.length === 0 ? `
|
|
51128
|
+
Done. Start your app as usual (\`${runScript(config2.packageManager, config2.scripts.dev ?? "dev")}\`) and click the Revyse button.` : `
|
|
51129
|
+
Next: ${steps.join(", then ")}. Then start your app as usual.`
|
|
51130
|
+
);
|
|
50984
51131
|
}
|
|
51132
|
+
return { status, ...configured, connection };
|
|
51133
|
+
}
|
|
51134
|
+
async function configure(opts, io, ask) {
|
|
51135
|
+
const { root } = opts;
|
|
51136
|
+
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
50985
51137
|
const { config: config2, warnings } = detectProject(root);
|
|
50986
51138
|
io.log(`Detected ${config2.framework} (${config2.packageManager})`);
|
|
50987
51139
|
io.log(`Source directories: ${config2.sourceDirs.join(", ")}`);
|
|
@@ -50989,7 +51141,7 @@ async function runInit(opts, io) {
|
|
|
50989
51141
|
io.log(DISCLOSURE_TEXT);
|
|
50990
51142
|
if (!await ask("Do you accept how Revyse handles your code?")) {
|
|
50991
51143
|
io.log("Nothing was changed. Run `revyse init` again if you change your mind.");
|
|
50992
|
-
return
|
|
51144
|
+
return null;
|
|
50993
51145
|
}
|
|
50994
51146
|
const accepted = {
|
|
50995
51147
|
...config2,
|
|
@@ -51022,46 +51174,146 @@ Revyse needs this change to ${file2} (it only has an effect in \`next dev\`):
|
|
|
51022
51174
|
);
|
|
51023
51175
|
if (await ask(`Apply this change to ${file2}?`))
|
|
51024
51176
|
nextConfigEdit = { file: file2, code: result.code };
|
|
51025
|
-
else
|
|
51026
|
-
io.log(
|
|
51027
|
-
`Skipped. Wrap the export of ${file2} with withRevyse() yourself before \`revyse dev\`.`
|
|
51028
|
-
);
|
|
51177
|
+
else io.log(`Skipped. Wrap the export of ${file2} with withRevyse() yourself.`);
|
|
51029
51178
|
}
|
|
51030
51179
|
}
|
|
51031
51180
|
}
|
|
51032
|
-
|
|
51181
|
+
writeFileSync6(path28.join(root, CONFIG_FILE), `${JSON.stringify(accepted, null, 2)}
|
|
51033
51182
|
`);
|
|
51034
|
-
if (nextConfigEdit)
|
|
51183
|
+
if (nextConfigEdit) writeFileSync6(path28.join(root, nextConfigEdit.file), nextConfigEdit.code);
|
|
51035
51184
|
if (ensureGitignored(root))
|
|
51036
51185
|
io.log(`Added ${GITIGNORE_ENTRY} to .gitignore (local change history).`);
|
|
51037
51186
|
io.log(`
|
|
51038
51187
|
Wrote ${CONFIG_FILE}.`);
|
|
51039
|
-
|
|
51040
|
-
|
|
51041
|
-
|
|
51042
|
-
|
|
51188
|
+
return { config: accepted, nextConfigEdited: Boolean(nextConfigEdit) };
|
|
51189
|
+
}
|
|
51190
|
+
async function installPackages(root, config2, setup2, io, ask) {
|
|
51191
|
+
const missing = PACKAGES.filter((name) => !devDependencies(root)[name]);
|
|
51192
|
+
if (missing.length === 0) return true;
|
|
51193
|
+
const specs = missing.map((name) => `${name}@${VERSION}`);
|
|
51194
|
+
const command = installCommand(config2.packageManager, specs).join(" ");
|
|
51195
|
+
if (!await ask(`Install ${missing.join(" and ")} as dev dependencies (${command})?`)) {
|
|
51196
|
+
io.log(`Skipped. Install them yourself: ${command}`);
|
|
51197
|
+
return false;
|
|
51198
|
+
}
|
|
51199
|
+
try {
|
|
51200
|
+
await setup2.install(root, config2.packageManager, specs);
|
|
51201
|
+
io.log(`Installed ${missing.join(" and ")}.`);
|
|
51202
|
+
return true;
|
|
51203
|
+
} catch (err) {
|
|
51204
|
+
io.warn(`Couldn't install them (${err.message}). Run: ${command}`);
|
|
51205
|
+
return false;
|
|
51043
51206
|
}
|
|
51044
|
-
|
|
51207
|
+
}
|
|
51208
|
+
async function connectProject(root, config2, setup2, io, ask) {
|
|
51209
|
+
const existing = await setup2.connection(root).catch(() => null);
|
|
51210
|
+
if (existing) {
|
|
51211
|
+
io.log(`Connected to Revyse: project "${existing.project}" (${existing.email}).`);
|
|
51212
|
+
return existing;
|
|
51213
|
+
}
|
|
51214
|
+
if (!await ask(
|
|
51215
|
+
"Connect this app to Revyse? Your browser opens to sign in and create the project."
|
|
51216
|
+
))
|
|
51217
|
+
return null;
|
|
51218
|
+
try {
|
|
51219
|
+
const connection = await setup2.connect(root, {
|
|
51220
|
+
name: projectName(root),
|
|
51221
|
+
framework: config2.framework,
|
|
51222
|
+
packageManager: config2.packageManager
|
|
51223
|
+
});
|
|
51224
|
+
io.log(`Connected to Revyse: project "${connection.project}" (${connection.email}).`);
|
|
51225
|
+
return connection;
|
|
51226
|
+
} catch (err) {
|
|
51227
|
+
io.warn(`Couldn't connect to Revyse (${err.message}).`);
|
|
51228
|
+
return null;
|
|
51229
|
+
}
|
|
51230
|
+
}
|
|
51231
|
+
function projectName(root) {
|
|
51232
|
+
const name = readPackageJson(root)?.name?.replace(/^@[^/]+\//, "").trim();
|
|
51233
|
+
return (name || path28.basename(root)).slice(0, 100);
|
|
51045
51234
|
}
|
|
51046
51235
|
function ensureGitignored(root) {
|
|
51047
|
-
const file2 =
|
|
51048
|
-
const current =
|
|
51236
|
+
const file2 = path28.join(root, ".gitignore");
|
|
51237
|
+
const current = existsSync7(file2) ? readFileSync12(file2, "utf8") : "";
|
|
51049
51238
|
const lines = current.split(/\r?\n/).map((l) => l.trim());
|
|
51050
51239
|
if (lines.includes(GITIGNORE_ENTRY) || lines.includes(".revyse")) return false;
|
|
51051
51240
|
const separator = current === "" ? "" : current.endsWith("\n") ? "\n" : "\n\n";
|
|
51052
|
-
|
|
51241
|
+
writeFileSync6(file2, `${current}${separator}# Revyse local state
|
|
51053
51242
|
${GITIGNORE_ENTRY}
|
|
51054
51243
|
`);
|
|
51055
51244
|
return true;
|
|
51056
51245
|
}
|
|
51057
|
-
function
|
|
51246
|
+
function readPackageJson(root) {
|
|
51058
51247
|
try {
|
|
51059
|
-
|
|
51060
|
-
return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
|
|
51248
|
+
return JSON.parse(readFileSync12(path28.join(root, "package.json"), "utf8"));
|
|
51061
51249
|
} catch {
|
|
51062
|
-
return
|
|
51250
|
+
return null;
|
|
51063
51251
|
}
|
|
51064
51252
|
}
|
|
51253
|
+
var devDependencies = (root) => readPackageJson(root)?.devDependencies ?? {};
|
|
51254
|
+
|
|
51255
|
+
// src/connect.ts
|
|
51256
|
+
function openBrowser(url2) {
|
|
51257
|
+
if (process.env.BROWSER === "none") return;
|
|
51258
|
+
const [command, args] = process.platform === "darwin" ? ["open", [url2]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url2.replace(/&/g, "^&")]] : ["xdg-open", [url2]];
|
|
51259
|
+
try {
|
|
51260
|
+
const child = spawn2(command, args, { stdio: "ignore", detached: true });
|
|
51261
|
+
child.on("error", () => {
|
|
51262
|
+
});
|
|
51263
|
+
child.unref();
|
|
51264
|
+
} catch {
|
|
51265
|
+
}
|
|
51266
|
+
}
|
|
51267
|
+
function activationUrl(verificationUriComplete, suggestion) {
|
|
51268
|
+
if (!suggestion) return verificationUriComplete;
|
|
51269
|
+
const url2 = new URL(verificationUriComplete);
|
|
51270
|
+
url2.searchParams.set("name", suggestion.name);
|
|
51271
|
+
url2.searchParams.set("framework", suggestion.framework);
|
|
51272
|
+
url2.searchParams.set("pm", suggestion.packageManager);
|
|
51273
|
+
return url2.toString();
|
|
51274
|
+
}
|
|
51275
|
+
async function connectInBrowser(opts) {
|
|
51276
|
+
const { token, projectId } = await deviceLogin({
|
|
51277
|
+
client: createApiClient({ apiUrl: opts.apiUrl }),
|
|
51278
|
+
clientName: `revyse-cli on ${hostname3()}`,
|
|
51279
|
+
onCode: ({ userCode, verificationUriComplete }) => {
|
|
51280
|
+
const url2 = activationUrl(verificationUriComplete, opts.suggestion);
|
|
51281
|
+
opts.log(`
|
|
51282
|
+
Your browser is opening to connect this app to Revyse. If it doesn't, go to:
|
|
51283
|
+
`);
|
|
51284
|
+
opts.log(` ${url2}
|
|
51285
|
+
`);
|
|
51286
|
+
opts.log(`and check the code matches: ${userCode}
|
|
51287
|
+
`);
|
|
51288
|
+
opts.log("Waiting for you to approve it\u2026");
|
|
51289
|
+
(opts.open ?? openBrowser)(url2);
|
|
51290
|
+
}
|
|
51291
|
+
});
|
|
51292
|
+
saveCredential(opts.root, { apiUrl: opts.apiUrl, token, projectId });
|
|
51293
|
+
const me = await createApiClient({ apiUrl: opts.apiUrl, token }).get("/api/cli/whoami");
|
|
51294
|
+
return { email: me.user.email, project: me.project.name };
|
|
51295
|
+
}
|
|
51296
|
+
async function currentConnection(root) {
|
|
51297
|
+
const cred = loadCredential(root);
|
|
51298
|
+
if (!cred) return null;
|
|
51299
|
+
try {
|
|
51300
|
+
const me = await createApiClient(cred).get("/api/cli/whoami");
|
|
51301
|
+
return { email: me.user.email, project: me.project.name };
|
|
51302
|
+
} catch (err) {
|
|
51303
|
+
if (err instanceof ApiError && (err.status === 401 || err.status === 404)) return null;
|
|
51304
|
+
throw err;
|
|
51305
|
+
}
|
|
51306
|
+
}
|
|
51307
|
+
function initSetup(apiUrl, log) {
|
|
51308
|
+
return {
|
|
51309
|
+
install: async (root, pm, specs) => {
|
|
51310
|
+
const [command, ...args] = installCommand(pm, specs);
|
|
51311
|
+
await execa(command, args, { cwd: root, stdio: "inherit" });
|
|
51312
|
+
},
|
|
51313
|
+
connection: currentConnection,
|
|
51314
|
+
connect: (root, suggestion) => connectInBrowser({ root, apiUrl, suggestion, log })
|
|
51315
|
+
};
|
|
51316
|
+
}
|
|
51065
51317
|
|
|
51066
51318
|
// src/prompt.ts
|
|
51067
51319
|
import { createInterface } from "readline";
|
|
@@ -51092,18 +51344,29 @@ function createPrompter(input2 = process.stdin, output2 = process.stdout) {
|
|
|
51092
51344
|
}
|
|
51093
51345
|
|
|
51094
51346
|
// src/commands/init.ts
|
|
51095
|
-
var initCommand = new Command("init").description(
|
|
51096
|
-
|
|
51097
|
-
|
|
51098
|
-
|
|
51099
|
-
|
|
51100
|
-
|
|
51101
|
-
|
|
51102
|
-
|
|
51103
|
-
|
|
51104
|
-
|
|
51347
|
+
var initCommand = new Command("init").description(
|
|
51348
|
+
"Set Revyse up in this app: the data disclosure, next.config, the packages, and the project on Revyse"
|
|
51349
|
+
).option("--force", "re-run the disclosure and next.config steps when revyse.config.json exists").option("-y, --yes", "accept the disclosure and proposed edits without prompting").option("--no-connect", "set up without connecting to Revyse (CI, offline)").option("--api-url <url>", "Revyse API URL (default: $REVYSE_API_URL or https://revyse.dev)").action(
|
|
51350
|
+
async (options) => {
|
|
51351
|
+
const prompter = createPrompter();
|
|
51352
|
+
const log = (m) => console.log(m);
|
|
51353
|
+
try {
|
|
51354
|
+
const outcome = await runInit(
|
|
51355
|
+
{
|
|
51356
|
+
root: process.cwd(),
|
|
51357
|
+
force: options.force,
|
|
51358
|
+
yes: options.yes,
|
|
51359
|
+
connect: options.connect,
|
|
51360
|
+
setup: initSetup(resolveApiUrl(options.apiUrl), log)
|
|
51361
|
+
},
|
|
51362
|
+
{ log, warn: (m) => console.warn(m), confirm: prompter.confirm }
|
|
51363
|
+
);
|
|
51364
|
+
if (outcome.status === "declined") process.exitCode = 1;
|
|
51365
|
+
} finally {
|
|
51366
|
+
prompter.close();
|
|
51367
|
+
}
|
|
51105
51368
|
}
|
|
51106
|
-
|
|
51369
|
+
);
|
|
51107
51370
|
|
|
51108
51371
|
// src/commands/change.ts
|
|
51109
51372
|
var changeCommand = new Command("change").description("Headless change operations");
|
|
@@ -51117,7 +51380,7 @@ var common2 = (cmd) => cmd.requiredOption("--api-url <url>", "Revyse API URL").r
|
|
|
51117
51380
|
function setup(opts) {
|
|
51118
51381
|
const root = process.cwd();
|
|
51119
51382
|
const config2 = projectConfigSchema.parse(
|
|
51120
|
-
JSON.parse(
|
|
51383
|
+
JSON.parse(readFileSync13(path29.join(root, CONFIG_FILE), "utf8"))
|
|
51121
51384
|
);
|
|
51122
51385
|
const token = opts.token ?? process.env.REVYSE_TOKEN;
|
|
51123
51386
|
if (!token) throw new Error("A project token is required (--token or REVYSE_TOKEN)");
|