@backifydev/cli 0.2.0 → 0.2.1
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 +12 -7
- package/dist/index.js +85 -7
- package/package.json +29 -29
package/README.md
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
# @backifydev/cli
|
|
2
|
-
|
|
3
|
-
Set up and check a Backify application repository.
|
|
4
|
-
|
|
5
|
-
```sh
|
|
6
|
-
npx @backifydev/cli init --app app_xxxxx
|
|
1
|
+
# @backifydev/cli
|
|
2
|
+
|
|
3
|
+
Set up and check a Backify application repository.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx @backifydev/cli init --app app_xxxxx
|
|
7
7
|
npx @backifydev/cli doctor
|
|
8
8
|
npx @backifydev/cli sync
|
|
9
|
+
npx @backifydev/cli login
|
|
9
10
|
```
|
|
10
11
|
|
|
12
|
+
`login` opens the Backify portal in your browser. Sign in and select an organization; the portal returns the organization and access token to the CLI through a random, short-lived callback bound to `127.0.0.1`. The CLI stores them in `~/.backify.json`. Set `portalUrl` in that file to use another portal, or `BACKIFY_PORTAL_URL` in the environment. `login <token> [organizationId]` remains available for scripted development.
|
|
13
|
+
|
|
11
14
|
`init` installs `@backifydev/sdk`, writes `backify.json`, and maintains the Backify blocks in `AGENTS.md`, `CLAUDE.md`, and `.github/copilot-instructions.md`. It preserves other instructions in those files.
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
You can start from an empty folder without a Git repository. `init` creates a minimal private `package.json` before installing the SDK, without choosing a framework. Add the application's framework and development/build scripts afterward. Existing manifests are preserved; in a non-empty application or monorepo, run `init` from the appropriate JavaScript/TypeScript package directory.
|
|
17
|
+
|
|
18
|
+
See the [agent setup guide](https://docs.backify.dev/setup).
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,13 @@
|
|
|
4
4
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, rmSync } from "node:fs";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { join as join2 } from "node:path";
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { createServer } from "node:http";
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
7
10
|
|
|
8
11
|
// src/setup.ts
|
|
9
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
10
|
-
import { dirname, join } from "node:path";
|
|
12
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from "node:fs";
|
|
13
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
11
14
|
import { spawnSync } from "node:child_process";
|
|
12
15
|
|
|
13
16
|
// ../platform/dist/index.js
|
|
@@ -111,9 +114,17 @@ function installSdk(root = process.cwd()) {
|
|
|
111
114
|
const manager = detectPackageManager(root), args2 = manager === "npm" ? ["install", SDK_PACKAGE] : ["add", SDK_PACKAGE], result = spawnSync(manager, args2, { cwd: root, stdio: "inherit", shell: process.platform === "win32" });
|
|
112
115
|
if (result.status !== 0) throw new Error(`Could not install ${SDK_PACKAGE} with ${manager}`);
|
|
113
116
|
}
|
|
117
|
+
function ensurePackageManifest(root) {
|
|
118
|
+
const path = join(root, "package.json");
|
|
119
|
+
if (existsSync(path)) return;
|
|
120
|
+
const setupFiles = /* @__PURE__ */ new Set([".git", ".gitignore", ".github", "AGENTS.md", "CLAUDE.md", "README.md", "LICENSE", "backify.json"]);
|
|
121
|
+
if (readdirSync(root).some((file) => !setupFiles.has(file))) throw new Error("package.json is missing. Run backify init from the existing application's JavaScript/TypeScript package directory, or start in an empty folder. See https://docs.backify.dev/setup");
|
|
122
|
+
const name = basename(resolve(root)).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "backify-app";
|
|
123
|
+
writeFileSync(path, JSON.stringify({ name, version: "0.0.0", private: true, type: "module" }, null, 2) + "\n", { flag: "wx" });
|
|
124
|
+
}
|
|
114
125
|
function initialize(appId, root = process.cwd(), install = true) {
|
|
115
126
|
if (!/^app_[A-Za-z0-9_-]+$/.test(appId)) throw new Error("Provide an application ID using --app app_xxxxx");
|
|
116
|
-
|
|
127
|
+
ensurePackageManifest(root);
|
|
117
128
|
if (install) installSdk(root);
|
|
118
129
|
writeConfig(appId, root);
|
|
119
130
|
updateInstructions(root);
|
|
@@ -160,6 +171,67 @@ var option = (name) => {
|
|
|
160
171
|
const index = args.indexOf(name);
|
|
161
172
|
return index >= 0 ? args[index + 1] : void 0;
|
|
162
173
|
};
|
|
174
|
+
function openBrowser(url) {
|
|
175
|
+
const command2 = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open", args2 = process.platform === "win32" ? ["/c", "start", "", '"' + url + '"'] : [url];
|
|
176
|
+
const child = spawn(command2, args2, { stdio: "ignore", detached: true, shell: process.platform === "win32" });
|
|
177
|
+
child.unref();
|
|
178
|
+
}
|
|
179
|
+
async function browserLogin() {
|
|
180
|
+
const state = randomBytes(24).toString("base64url"), server = createServer((req, res) => {
|
|
181
|
+
if (req.method === "OPTIONS") {
|
|
182
|
+
res.writeHead(204, { "access-control-allow-origin": "*", "access-control-allow-methods": "POST, OPTIONS", "access-control-allow-headers": "content-type" });
|
|
183
|
+
return res.end();
|
|
184
|
+
}
|
|
185
|
+
if (req.method !== "POST" || req.url !== "/callback") {
|
|
186
|
+
res.writeHead(404);
|
|
187
|
+
return res.end();
|
|
188
|
+
}
|
|
189
|
+
let raw = "";
|
|
190
|
+
req.setEncoding("utf8");
|
|
191
|
+
req.on("data", (part) => raw += part);
|
|
192
|
+
req.on("end", () => {
|
|
193
|
+
try {
|
|
194
|
+
const value = JSON.parse(raw);
|
|
195
|
+
if (value.state !== state || typeof value.token !== "string" || typeof value.organizationId !== "string") throw new Error("Invalid callback");
|
|
196
|
+
res.writeHead(200, { "access-control-allow-origin": "*", "content-type": "text/plain" });
|
|
197
|
+
res.end("Backify CLI connected. You can close this tab.");
|
|
198
|
+
userConfig.token = value.token;
|
|
199
|
+
userConfig.organizationId = value.organizationId;
|
|
200
|
+
writeFileSync2(configPath, JSON.stringify(userConfig, null, 2));
|
|
201
|
+
server.close();
|
|
202
|
+
resolveLogin();
|
|
203
|
+
} catch {
|
|
204
|
+
res.writeHead(400, { "access-control-allow-origin": "*" });
|
|
205
|
+
res.end("Invalid or expired Backify CLI login.");
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
let resolveLogin = () => {
|
|
210
|
+
};
|
|
211
|
+
const completed = new Promise((resolve2) => resolveLogin = resolve2);
|
|
212
|
+
server.listen(0, "127.0.0.1", () => {
|
|
213
|
+
const address = server.address();
|
|
214
|
+
if (!address || typeof address === "string") throw new Error("Could not start local login callback");
|
|
215
|
+
const callback = `http://127.0.0.1:${address.port}/callback`;
|
|
216
|
+
const portal = new URL(process.env.BACKIFY_PORTAL_URL ?? userConfig.portalUrl ?? "https://backify.dev");
|
|
217
|
+
portal.searchParams.set("cli_callback", callback);
|
|
218
|
+
portal.searchParams.set("cli_state", state);
|
|
219
|
+
console.log(`Opening ${portal.origin} to sign in to Backify\u2026`);
|
|
220
|
+
openBrowser(portal.toString());
|
|
221
|
+
});
|
|
222
|
+
const timeout = setTimeout(() => {
|
|
223
|
+
server.close();
|
|
224
|
+
rejectLogin(new Error("Backify login timed out. Run backify login to try again."));
|
|
225
|
+
}, 5 * 6e4);
|
|
226
|
+
let rejectLogin = () => {
|
|
227
|
+
};
|
|
228
|
+
const failed = new Promise((_, reject) => rejectLogin = reject);
|
|
229
|
+
try {
|
|
230
|
+
await Promise.race([completed, failed]);
|
|
231
|
+
} finally {
|
|
232
|
+
clearTimeout(timeout);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
163
235
|
async function main() {
|
|
164
236
|
switch (command) {
|
|
165
237
|
case "init":
|
|
@@ -186,10 +258,16 @@ async function main() {
|
|
|
186
258
|
break;
|
|
187
259
|
}
|
|
188
260
|
case "login":
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
261
|
+
if (args[0]) {
|
|
262
|
+
userConfig.token = args[0];
|
|
263
|
+
userConfig.organizationId = args[1] ?? userConfig.organizationId;
|
|
264
|
+
writeFileSync2(configPath, JSON.stringify(userConfig, null, 2));
|
|
265
|
+
console.log("Logged in to Backify");
|
|
266
|
+
} else {
|
|
267
|
+
await browserLogin();
|
|
268
|
+
console.log("Logged in to Backify");
|
|
269
|
+
}
|
|
270
|
+
;
|
|
193
271
|
break;
|
|
194
272
|
case "logout":
|
|
195
273
|
rmSync(configPath, { force: true });
|
package/package.json
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@backifydev/cli",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Command-line setup and diagnostics for Backify applications",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": { "backify": "
|
|
7
|
-
"files": ["dist/index.js", "README.md"],
|
|
8
|
-
"scripts": {
|
|
9
|
-
"prebuild": "npm run build --workspace @backify/platform",
|
|
10
|
-
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js",
|
|
11
|
-
"prepack": "npm run build",
|
|
12
|
-
"pretypecheck": "npm run build --workspace @backify/platform",
|
|
13
|
-
"typecheck": "tsc --noEmit",
|
|
14
|
-
"pretest": "npm run build --workspace @backify/platform",
|
|
15
|
-
"test": "vitest run"
|
|
16
|
-
},
|
|
17
|
-
"publishConfig": { "access": "public" },
|
|
18
|
-
"repository": { "type": "git", "url": "git+https://github.com/frejnorling/backify.git", "directory": "packages/cli" },
|
|
19
|
-
"homepage": "https://docs.backify.dev/setup",
|
|
20
|
-
"license": "MIT",
|
|
21
|
-
"engines": { "node": ">=20" },
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"@backify/platform": "*",
|
|
24
|
-
"@types/node": "^24.3.1",
|
|
25
|
-
"esbuild": "^0.25.10",
|
|
26
|
-
"typescript": "^5.9.2",
|
|
27
|
-
"vitest": "^
|
|
28
|
-
}
|
|
29
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@backifydev/cli",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Command-line setup and diagnostics for Backify applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": { "backify": "dist/index.js" },
|
|
7
|
+
"files": ["dist/index.js", "README.md"],
|
|
8
|
+
"scripts": {
|
|
9
|
+
"prebuild": "npm run build --workspace @backify/platform",
|
|
10
|
+
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js",
|
|
11
|
+
"prepack": "npm run build",
|
|
12
|
+
"pretypecheck": "npm run build --workspace @backify/platform",
|
|
13
|
+
"typecheck": "tsc --noEmit",
|
|
14
|
+
"pretest": "npm run build --workspace @backify/platform",
|
|
15
|
+
"test": "vitest run"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": { "access": "public" },
|
|
18
|
+
"repository": { "type": "git", "url": "git+https://github.com/frejnorling/backify.git", "directory": "packages/cli" },
|
|
19
|
+
"homepage": "https://docs.backify.dev/setup",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"engines": { "node": ">=20" },
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@backify/platform": "*",
|
|
24
|
+
"@types/node": "^24.3.1",
|
|
25
|
+
"esbuild": "^0.25.10",
|
|
26
|
+
"typescript": "^5.9.2",
|
|
27
|
+
"vitest": "^4.1.11"
|
|
28
|
+
}
|
|
29
|
+
}
|