@backifydev/cli 0.3.6 → 0.3.8

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 (3) hide show
  1. package/README.md +2 -0
  2. package/dist/index.js +125 -23
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -276,3 +276,5 @@ Copy the application AI prompt in Backify to receive the application ID, organiz
276
276
  `ship` runs configured checks, verifies the repository/branch, and pushes already-committed source using a short-lived repository-scoped GitHub App credential. It never stages, commits, force-pushes, or fabricates preview testing. It waits for the exact healthy preview, then publishes when policy and permissions allow. Required testing/approval returns a pending state and preview URL, not a false successful production deployment. Use `--preview-only` to stop at preview, or `--timeout <seconds>` to change the default 1200-second wait. Applications must currently be at the repository root; Backify Cloud needs static index.html output, and customer Azure needs a Dockerfile serving port 8080.
277
277
 
278
278
  `disconnect` revokes that user's application CLI sessions. `logout` removes local credentials only. Prompt histories and local reports remain subject to application permissions. Deploy the matching API and publish CLI 0.3.0 before enabling this flow in production.
279
+
280
+ Connections now report the device hostname, operating system, architecture, and installed CLI version. Browser-based `connect` exchanges a general sign-in token for an application-scoped device session. Application **People & access → CLI access** shows these sessions and lets access managers revoke individual devices; source managers can revoke their own. Existing connections may need reconnecting to record device details. See [CLI access and country lookup configuration](../../docs/cli-access.md) for privacy, permissions and deployment requirements.
package/dist/index.js CHANGED
@@ -17593,6 +17593,28 @@ function unzipSync(data, opts) {
17593
17593
  import { createRequire as createRequire2 } from "node:module";
17594
17594
  import { dirname, resolve } from "node:path";
17595
17595
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
17596
+ async function deployFunctions(call2, appId, environmentId, packageBytes, options = {}) {
17597
+ const result = await call2("/apps/" + appId + "/functions/environments/" + environmentId + "/deploy?confirm=true", { method: "POST", headers: { "content-type": "application/zip" }, body: packageBytes });
17598
+ if (result.status === "succeeded") return;
17599
+ if (!result.operationId) throw new Error("Function deployment did not return a tracking operation. Success has not been verified.");
17600
+ const now = options.now ?? Date.now;
17601
+ const deadline = now() + (options.timeoutMs ?? 20 * 6e4);
17602
+ const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve24) => setTimeout(resolve24, milliseconds)));
17603
+ options.progress?.("Function deployment queued. Waiting for operation " + result.operationId + ".");
17604
+ while (now() < deadline) {
17605
+ let operation;
17606
+ try {
17607
+ operation = await call2("/operations/" + result.operationId);
17608
+ } catch (cause) {
17609
+ throw new Error("Could not read function deployment operation " + result.operationId + ". Check its status before uploading again: " + String(cause));
17610
+ }
17611
+ if (operation.status === "succeeded") return;
17612
+ if (operation.status === "failed" || operation.status === "cancelled") throw new Error("Function deployment " + operation.status + ": " + (operation.errorMessage ?? result.operationId));
17613
+ if (operation.status !== "pending" && operation.status !== "running") throw new Error("Unknown function deployment status: " + operation.status);
17614
+ await sleep(Math.min(options.intervalMs ?? 2e3, Math.max(0, deadline - now())));
17615
+ }
17616
+ throw new Error("Stopped waiting for function deployment. Operation " + result.operationId + " continues on Backify; check its status before uploading again.");
17617
+ }
17596
17618
  async function packageFunctions(entry, output) {
17597
17619
  const entryPath = resolve(entry);
17598
17620
  if (!existsSync(entryPath)) throw new Error("Function entrypoint does not exist: " + entryPath);
@@ -17638,6 +17660,71 @@ async function packageFunctions(entry, output) {
17638
17660
  return outputPath;
17639
17661
  }
17640
17662
 
17663
+ // src/cli-session.ts
17664
+ import { hostname, platform, release, arch } from "node:os";
17665
+
17666
+ // package.json
17667
+ var package_default = {
17668
+ name: "@backifydev/cli",
17669
+ version: "0.3.8",
17670
+ description: "Command-line setup and diagnostics for Backify applications",
17671
+ type: "module",
17672
+ bin: {
17673
+ backify: "dist/index.js"
17674
+ },
17675
+ files: [
17676
+ "dist/index.js",
17677
+ "README.md"
17678
+ ],
17679
+ scripts: {
17680
+ prebuild: "npm run build -w @backify/platform && npm run build -w @backifydev/sdk && npm run build -w @backify/analyzer",
17681
+ build: `esbuild src/index.ts --bundle --platform=node --format=esm --external:esbuild --external:typescript --outfile=dist/index.js --banner:js="import { createRequire as __backifyCreateRequire } from 'node:module'; var require = __backifyCreateRequire(import.meta.url);"`,
17682
+ prepack: "npm run build",
17683
+ pretypecheck: "npm run build -w @backify/platform && npm run build -w @backifydev/sdk && npm run build -w @backify/analyzer",
17684
+ typecheck: "tsc --noEmit",
17685
+ pretest: "npm run build -w @backify/platform && npm run build -w @backifydev/sdk && npm run build -w @backify/analyzer",
17686
+ test: "vitest run"
17687
+ },
17688
+ publishConfig: {
17689
+ access: "public"
17690
+ },
17691
+ repository: {
17692
+ type: "git",
17693
+ url: "git+https://github.com/frejnorling/backify.git",
17694
+ directory: "packages/cli"
17695
+ },
17696
+ homepage: "https://agent.backify.dev/",
17697
+ license: "MIT",
17698
+ engines: {
17699
+ node: ">=20"
17700
+ },
17701
+ dependencies: {
17702
+ "@azure/functions": "^4.7.2",
17703
+ "@backifydev/sdk": "^0.1.1",
17704
+ esbuild: "^0.25.10",
17705
+ fflate: "^0.8.2",
17706
+ typescript: "^5.9.2"
17707
+ },
17708
+ devDependencies: {
17709
+ "@backify/analyzer": "*",
17710
+ "@backify/platform": "*",
17711
+ "@hono/node-server": "2.1.3",
17712
+ "@types/node": "^24.3.1",
17713
+ hono: "4.13.12",
17714
+ vitest: "^4.1.11"
17715
+ }
17716
+ };
17717
+
17718
+ // src/cli-session.ts
17719
+ function cliDevice() {
17720
+ return { deviceName: hostname(), operatingSystem: platform() + " " + release(), architecture: arch(), cliVersion: package_default.version };
17721
+ }
17722
+ var cliUserAgent = "BackifyCLI/" + package_default.version;
17723
+ async function registerCliSession(client, applicationId, apiUrl) {
17724
+ const session = await client("/apps/" + applicationId + "/developer/session", { method: "POST", body: JSON.stringify(cliDevice()) });
17725
+ return { ...session, apiUrl };
17726
+ }
17727
+
17641
17728
  // src/local-runtime.ts
17642
17729
  import { createHash, randomUUID } from "node:crypto";
17643
17730
  import { chmodSync, existsSync as existsSync2, lstatSync, mkdirSync as mkdirSync2, readFileSync, readdirSync, writeFileSync as writeFileSync2 } from "node:fs";
@@ -17647,13 +17734,13 @@ import { spawnSync } from "node:child_process";
17647
17734
 
17648
17735
  // src/local-state.ts
17649
17736
  import { renameSync } from "node:fs";
17650
- function replaceLocalFile(source, destination, rename, platform, wait) {
17737
+ function replaceLocalFile(source, destination, rename, platform2, wait) {
17651
17738
  for (let attempt = 0; ; attempt++) {
17652
17739
  try {
17653
17740
  rename(source, destination);
17654
17741
  return;
17655
17742
  } catch (error) {
17656
- if (platform !== "win32" || attempt >= 4 || !(error instanceof Error) || !("code" in error) || !["EPERM", "EACCES", "EBUSY"].includes(String(error.code))) throw error;
17743
+ if (platform2 !== "win32" || attempt >= 4 || !(error instanceof Error) || !("code" in error) || !["EPERM", "EACCES", "EBUSY"].includes(String(error.code))) throw error;
17657
17744
  wait(10 * 2 ** attempt);
17658
17745
  }
17659
17746
  }
@@ -17666,8 +17753,8 @@ function renameLocalFile(source, destination) {
17666
17753
  var maxArchiveBytes = 256 * 1024 * 1024;
17667
17754
  var maxExpandedBytes = 512 * 1024 * 1024;
17668
17755
  var hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
17669
- function runtimeIdentifier(platform = process.platform, architecture = process.arch) {
17670
- const system = { win32: "win", darwin: "osx", linux: "linux" }[platform];
17756
+ function runtimeIdentifier(platform2 = process.platform, architecture = process.arch) {
17757
+ const system = { win32: "win", darwin: "osx", linux: "linux" }[platform2];
17671
17758
  if (!system || !["x64", "arm64"].includes(architecture)) throw new Error("Local runtime supports Windows, macOS and Linux on x64/arm64");
17672
17759
  return system + "-" + architecture;
17673
17760
  }
@@ -17825,7 +17912,7 @@ function parseApplicationManifest(source) {
17825
17912
  if (name2 === "database" && settings.migrations !== void 0 && !portablePath(settings.migrations))
17826
17913
  throw new Error("Migrations must be repository-relative");
17827
17914
  if (name2 === "storage" && settings.buckets !== void 0) {
17828
- if (!Array.isArray(settings.buckets) || settings.buckets.length > 50 || settings.buckets.some((bucket) => !object(bucket) || typeof bucket.name !== "string" || !/^[A-Za-z0-9_-]{1,100}$/.test(bucket.name) || bucket.access !== void 0 && bucket.access !== "private" || Object.keys(bucket).some((key) => !["name", "access"].includes(key))) || new Set(settings.buckets.map((bucket) => bucket.name)).size !== settings.buckets.length)
17915
+ if (!Array.isArray(settings.buckets) || settings.buckets.length > 50 || settings.buckets.some((bucket) => !object(bucket) || typeof bucket.name !== "string" || !/^[A-Za-z0-9_-]{1,100}$/.test(bucket.name) || bucket.access !== void 0 && bucket.access !== "private" || bucket.isolation !== void 0 && !["private", "user", "tenant", "authenticated", "public", "selected"].includes(String(bucket.isolation)) || bucket.allowSignedUrls !== void 0 && typeof bucket.allowSignedUrls !== "boolean" || Object.keys(bucket).some((key) => !["name", "access", "isolation", "allowSignedUrls"].includes(key))) || new Set(settings.buckets.map((bucket) => bucket.name)).size !== settings.buckets.length)
17829
17916
  throw new Error("Storage requires at most 50 distinct private logical buckets");
17830
17917
  }
17831
17918
  capabilities[name2] = structuredClone(settings);
@@ -17854,8 +17941,8 @@ function manifestDiagnostics(manifest, used) {
17854
17941
  // ../platform/dist/delivery.js
17855
17942
  var deliveryInstructions = `# Backify application delivery and prompt history
17856
17943
 
17857
- Instruction version: 4
17858
- Versioned guide: https://agent.backify.dev/v4/delivery.md
17944
+ Instruction version: 5
17945
+ Versioned guide: https://agent.backify.dev/v5/delivery.md
17859
17946
 
17860
17947
  These instructions are for AI coding tools building applications on Backify, not the Backify platform implementation. Complete the application and deliver a verified deployment, not just SDK setup. Respect the coding tool's own permissions and confirmation settings; preserve unrelated work and applicable repository instructions.
17861
17948
 
@@ -17873,7 +17960,7 @@ Initialize using App Id from the copied prompt and the current tool selection:
17873
17960
 
17874
17961
  npx @backifydev/cli@latest init --app <app-id> --agent <current-tool>
17875
17962
 
17876
- For a tool without a built-in preset, replace --agent <current-tool> with --instruction-file <relative-path>. Read BACKIFY.md and the instruction file used by the current AI coding tool before changing application code. Create or update only instruction files used by that tool; never create files for other coding tools. Use --agent for a built-in tool preset or --instruction-file <relative-path> for the current tool's native instruction file, as described in the setup guide. Initialization installs this workflow alongside the persistent SDK instructions. Follow https://agent.backify.dev/v4 for instruction version 4; use the guide matching backify.json in an existing application. Do not silently upgrade its managed instruction version or overwrite instructions outside the managed block.
17963
+ For a tool without a built-in preset, replace --agent <current-tool> with --instruction-file <relative-path>. Read BACKIFY.md and the instruction file used by the current AI coding tool before changing application code. Create or update only instruction files used by that tool; never create files for other coding tools. Use --agent for a built-in tool preset or --instruction-file <relative-path> for the current tool's native instruction file, as described in the setup guide. Initialization installs this workflow alongside the persistent SDK instructions. Follow https://agent.backify.dev/v5 for instruction version 5; use the guide matching backify.json in an existing application. Do not silently upgrade its managed instruction version or overwrite instructions outside the managed block.
17877
17964
 
17878
17965
  Run the following to discover the connected hosting target, repository, branch, reports, releases, and deployment failures:
17879
17966
 
@@ -17907,6 +17994,12 @@ For Backify Cloud, build a static application with a build script and index.html
17907
17994
 
17908
17995
  Do not claim unsupported hosted server functions or SDK execution paths work. Report unsupported runtime requirements instead of substituting another platform. Keep secrets server-side and out of browser code, project files, and Git.
17909
17996
 
17997
+ ## Local development before delivery
17998
+
17999
+ From the first setup, make every new application runnable and testable locally without a cloud deployment. Follow the matching setup guide's local development steps: preserve the framework, provide a package-manager dev script and README, use @backifydev/sdk/local for platform capabilities and tests, and keep local state and credentials out of Git. Use a native framework dev server for frontend-only apps; for Backify server capabilities, configure backify.yaml and npx @backifydev/cli@latest dev --mode lightweight --json with the supported frontend adapter when needed. Disclose emulator limits and any full-runtime prerequisites.
18000
+
18001
+ Start the application, wait for readiness, request the actual local application URL, and verify a representative user flow before shipping. Document safe seed data and local test identities where needed. Cloud connection, preview approval, or deployment failure must not prevent safe local testing; report those blockers separately without bypassing authorization. If requirements are missing, verify a minimal runnable shell before asking the required final question. Never claim local readiness from a build or management-dashboard response alone.
18002
+
17910
18003
  ## Verify, push, and publish
17911
18004
 
17912
18005
  Run:
@@ -17925,6 +18018,8 @@ If testing or approval is required, return the preview URL and explain the remai
17925
18018
 
17926
18019
  After setup or a delivery attempt, give the user concise instructions: explain what is ready, how to open and use the application, and any sign-in, testing, approval, or other next steps. Summarize actual checks and blockers without exposing credentials or asking the user to perform routine deployment work the coding tool can complete.
17927
18020
 
18021
+ Include the verified Local URL, exact start command, and local testing/sign-in steps even when cloud delivery is blocked. If local startup was not verified, say unavailable and explain the blocker.
18022
+
17928
18023
  Include separate Preview URL and Production URL lines, using the actual URLs returned by CLI status or deployment results. Include both URLs when available. If either is unavailable, say not deployed or unavailable and explain why; never invent a URL or claim an unverified deployment is live. When production still serves an earlier release, label it as the existing production version and explain that the new changes are preview-only or awaiting approval.
17929
18024
 
17930
18025
  If application requirements are missing after setup, finish the response with this exact question as its final line, without any text after it:
@@ -17935,7 +18030,7 @@ These instructions are an explicit agent workflow, not a universal chat intercep
17935
18030
  `;
17936
18031
 
17937
18032
  // ../platform/dist/index.js
17938
- var INSTRUCTION_VERSION = 4;
18033
+ var INSTRUCTION_VERSION = 5;
17939
18034
  var SDK_PACKAGE = "@backifydev/sdk";
17940
18035
  var DOCS_URL = "https://docs.backify.dev";
17941
18036
  var MAIL_LIMITS = Object.freeze({
@@ -18005,6 +18100,7 @@ Require explicit confirmation before destructive data operations or removing sta
18005
18100
  ## Implementation workflow
18006
18101
 
18007
18102
  Inspect the framework, lockfile, scripts, configuration, and applicable repository instructions before editing. Preserve the existing framework and unrelated work. Implement the requested application rather than stopping at SDK setup; ask what to build if requirements are missing.
18103
+ From the first setup, make every new application runnable and testable locally without a cloud deployment. Provide a package-manager start script, document prerequisites and local test identities, start the application, and verify its local URL and a representative user flow. Use \`@backifydev/sdk/local\` for local platform capabilities, keep local state and credentials out of Git, and clearly disclose emulation limits. Include the actual Local URL, start command, checks, and any blockers in the user handoff; never claim an unstarted application is ready. Follow the matching setup guide for framework-specific local commands.
18008
18104
  Run relevant tests, typechecking, lint, and builds using the repository's scripts when available. Report changes, checks, and remaining blockers accurately; do not claim unverified success.
18009
18105
 
18010
18106
  ## Connected application delivery
@@ -19794,7 +19890,7 @@ async function startLocalServices(root, manifest, options = {}) {
19794
19890
  if (databasePort === hcrPort) throw new Error("HCR and PostgreSQL require separate ports");
19795
19891
  const cluster = join4(directory2, "postgres");
19796
19892
  assertLocalStatePath(root, cluster);
19797
- const release = acquireLocalLease(root, ".backify/dev/services/lifecycle.lock");
19893
+ const release2 = acquireLocalLease(root, ".backify/dev/services/lifecycle.lock");
19798
19894
  let postgresStarted = false;
19799
19895
  let hcr;
19800
19896
  let timer;
@@ -19831,7 +19927,7 @@ async function startLocalServices(root, manifest, options = {}) {
19831
19927
  if (postgresStarted) notify("postgres", "stopped");
19832
19928
  if (existsSync5(tokenPath)) unlinkSync3(tokenPath);
19833
19929
  } finally {
19834
- release();
19930
+ release2();
19835
19931
  }
19836
19932
  })();
19837
19933
  const applied = [];
@@ -21211,7 +21307,7 @@ function initializeLocalSecrets(root, application, keyFile) {
21211
21307
  if (dirname8(current) === current) break;
21212
21308
  }
21213
21309
  ensureIgnored(root);
21214
- const release = acquireLocalLease(root, ".backify/dev/secrets.lock");
21310
+ const release2 = acquireLocalLease(root, ".backify/dev/secrets.lock");
21215
21311
  const key = randomBytes4(32);
21216
21312
  try {
21217
21313
  if (existsSync7(filename2)) throw failure();
@@ -21220,7 +21316,7 @@ function initializeLocalSecrets(root, application, keyFile) {
21220
21316
  writePrivate2(filename2, encrypt(root, application, key, {}));
21221
21317
  } finally {
21222
21318
  key.fill(0);
21223
- release();
21319
+ release2();
21224
21320
  }
21225
21321
  return { schemaVersion: 1, status: "initialized", keyFile: external, names: [] };
21226
21322
  }
@@ -21228,7 +21324,7 @@ function setLocalSecret(root, application, keyFile, name2, value) {
21228
21324
  if (!secretName.test(name2) || Buffer.byteLength(value) < 1 || Buffer.byteLength(value) > 65536) throw new Error("Local secrets require an uppercase logical name and 1-65536 UTF-8 bytes from stdin");
21229
21325
  const filename2 = vaultPath(root);
21230
21326
  assertLocalStatePath(root, filename2);
21231
- const release = acquireLocalLease(root, ".backify/dev/secrets.lock");
21327
+ const release2 = acquireLocalLease(root, ".backify/dev/secrets.lock");
21232
21328
  let key;
21233
21329
  try {
21234
21330
  const secrets = readLocalSecrets(root, application, keyFile);
@@ -21240,7 +21336,7 @@ function setLocalSecret(root, application, keyFile, name2, value) {
21240
21336
  renameLocalFile(temporary, filename2);
21241
21337
  } finally {
21242
21338
  key?.fill(0);
21243
- release();
21339
+ release2();
21244
21340
  }
21245
21341
  return { schemaVersion: 1, status: "stored", name: name2 };
21246
21342
  }
@@ -22635,7 +22731,7 @@ function apiClient(session, transport = fetch) {
22635
22731
  return async (path, init) => {
22636
22732
  if (!path.startsWith("/") || path.startsWith("//")) throw new Error("Invalid API path.");
22637
22733
  if (session.expiresAt && Date.parse(session.expiresAt) <= Date.now()) throw new Error("CLI session expired. Copy a new setup prompt or run backify login.");
22638
- const response = await transport(base + path, { ...init, redirect: "error", signal: AbortSignal.timeout(3e4), headers: { "content-type": "application/json", "X-Organization-ID": session.organizationId, ...session.token ? { authorization: "Bearer " + session.token } : {}, ...init?.headers } });
22734
+ const response = await transport(base + path, { ...init, redirect: "error", signal: AbortSignal.timeout(3e4), headers: { "content-type": "application/json", "user-agent": cliUserAgent, "X-Organization-ID": session.organizationId, ...session.token ? { authorization: "Bearer " + session.token } : {}, ...init?.headers } });
22639
22735
  if (!response.ok) {
22640
22736
  let message = "Backify request failed (" + response.status + ").";
22641
22737
  try {
@@ -22773,8 +22869,8 @@ function localReport(runChecks, root = process.cwd()) {
22773
22869
  const outputDirectory = ["dist", "build", "out", "."].find((directory2) => existsSync14(join16(root, directory2, "index.html"))) ?? null;
22774
22870
  return { framework, sdkVersion: dependencies["@backifydev/sdk"] ?? null, branch, commitSha, dirty, buildCommand: pkg.scripts?.build ? manager + " run build" : null, outputDirectory, hasDockerfile: existsSync14(join16(root, "Dockerfile")), checks };
22775
22871
  }
22776
- function matchesSource(release, commit) {
22777
- return release?.commitSha === commit || release?.sourceCommitSha === commit;
22872
+ function matchesSource(release2, commit) {
22873
+ return release2?.commitSha === commit || release2?.sourceCommitSha === commit;
22778
22874
  }
22779
22875
  function pushSource(appId, commit, branch, destination, root, environment) {
22780
22876
  const remoteHead = git2(["ls-remote", "--", destination, "refs/heads/" + branch], root, environment).split(/\s+/)[0];
@@ -22796,7 +22892,7 @@ async function waitForPreview(call2, prefix, commit, options = {}) {
22796
22892
  const sleep = options.sleep ?? ((delay) => new Promise((resolve24) => setTimeout(resolve24, delay)));
22797
22893
  do {
22798
22894
  const deployments = await call2(prefix + "/deployments");
22799
- const matching = deployments.find((release) => release.kind === "preview" && matchesSource(release, commit) && !options.previousDeploymentIds?.includes(release.id));
22895
+ const matching = deployments.find((release2) => release2.kind === "preview" && matchesSource(release2, commit) && !options.previousDeploymentIds?.includes(release2.id));
22800
22896
  if (matching?.status === "failed") throw new Error("Deployment of this source failed. Run backify status and inspect its deployment steps.");
22801
22897
  const overview = await call2(prefix + "/releases");
22802
22898
  if (matchesSource(overview.preview, commit) && overview.preview?.status === "succeeded" && overview.preview.healthResult === "healthy") return overview;
@@ -22833,7 +22929,7 @@ async function ship(call2, appId, options = {}) {
22833
22929
  const environment = gitEnvironment(status.repository, credential.token);
22834
22930
  const destination = "https://github.com/" + status.repository + ".git";
22835
22931
  const alreadyPushed = pushSource(appId, commit, status.branch, destination, root, environment);
22836
- const currentPreview = previousDeployments.find((release) => release.kind === "preview" && matchesSource(release, commit));
22932
+ const currentPreview = previousDeployments.find((release2) => release2.kind === "preview" && matchesSource(release2, commit));
22837
22933
  if (alreadyPushed && (!currentPreview || currentPreview.status === "failed") && !(matchesSource(status.releases.preview, commit) && status.releases.preview?.status === "succeeded")) await call2(prefix + "/deployments/preview", { method: "POST", body: JSON.stringify({ sourceRevision: "latest" }) });
22838
22934
  const overview = await waitForPreview(call2, prefix, commit, { timeoutMs: options.timeoutMs, previousDeploymentIds: previousDeployments.filter((deployment) => deployment.status === "failed").map((deployment) => deployment.id), previousBuildRunId: previousBuild.runId ?? null });
22839
22935
  if (overview.preview.commitSha !== commit) {
@@ -23070,7 +23166,7 @@ async function main() {
23070
23166
  const environmentId = option("--environment");
23071
23167
  const packagePath = option("--package");
23072
23168
  if (!environmentId || !packagePath || !args.includes("--confirm")) throw new Error("Use backify functions deploy --environment <environment-id> --package <functions.zip> --confirm. This replaces the complete function project.");
23073
- await call("/apps/" + appId + "/functions/environments/" + applicationGuid(environmentId) + "/deploy?confirm=true", { method: "POST", headers: { "content-type": "application/zip" }, body: new Uint8Array(readFileSync24(packagePath)) });
23169
+ await deployFunctions(call, appId, applicationGuid(environmentId), new Uint8Array(readFileSync24(packagePath)), { progress: (message) => console.log(message) });
23074
23170
  console.log("Functions deployed.");
23075
23171
  } else throw new Error("Use backify functions package or backify functions deploy");
23076
23172
  break;
@@ -23113,14 +23209,20 @@ async function main() {
23113
23209
  if (code) {
23114
23210
  if (!organizationId) throw new Error("The setup prompt must supply the organization.");
23115
23211
  const redeem = apiClient({ token: "", organizationId, apiUrl: address });
23116
- const paired = await redeem("/apps/" + applicationGuid(appId) + "/developer/pair", { method: "POST", body: JSON.stringify({ code }) });
23212
+ const paired = await redeem("/apps/" + applicationGuid(appId) + "/developer/pair", { method: "POST", body: JSON.stringify({ code, device: cliDevice() }) });
23117
23213
  userConfig.sessions ??= {};
23118
23214
  userConfig.sessions[key] = { ...paired, apiUrl: address };
23119
23215
  saveUserConfig(configPath, userConfig);
23120
23216
  } else if (!userConfig.sessions?.[key] && !userConfig.token) {
23121
23217
  await browserLogin();
23122
23218
  }
23123
- const session = userConfig.sessions?.[key] ?? { token: userConfig.token ?? "", organizationId: organizationId ?? userConfig.organizationId ?? "", apiUrl: address };
23219
+ let session = userConfig.sessions?.[key] ?? { token: userConfig.token ?? "", organizationId: organizationId ?? userConfig.organizationId ?? "", apiUrl: address };
23220
+ if (!session.token.startsWith("bfcli_")) {
23221
+ session = await registerCliSession(apiClient(session), applicationGuid(appId), session.apiUrl);
23222
+ userConfig.sessions ??= {};
23223
+ userConfig.sessions[key] = session;
23224
+ saveUserConfig(configPath, userConfig);
23225
+ }
23124
23226
  const client = apiClient(session);
23125
23227
  await client("/apps/" + applicationGuid(appId) + "/developer");
23126
23228
  userConfig.sessions ??= {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backifydev/cli",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Command-line setup and diagnostics for Backify applications",
5
5
  "type": "module",
6
6
  "bin": {