openpouch 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/openpouch.js +91 -53
- package/package.json +1 -1
package/openpouch.js
CHANGED
|
@@ -4259,9 +4259,8 @@ function findUsableApproval(file, match, now) {
|
|
|
4259
4259
|
}
|
|
4260
4260
|
|
|
4261
4261
|
// packages/cli/src/commands/activate.ts
|
|
4262
|
-
import { mkdir, writeFile as writeFile3 } from "node:fs/promises";
|
|
4263
|
-
import {
|
|
4264
|
-
import { join as join3 } from "node:path";
|
|
4262
|
+
import { mkdir, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
4263
|
+
import { dirname } from "node:path";
|
|
4265
4264
|
|
|
4266
4265
|
// packages/cli/src/shared.ts
|
|
4267
4266
|
import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
@@ -4832,12 +4831,17 @@ var PROVIDER_CREDENTIALS = {
|
|
|
4832
4831
|
};
|
|
4833
4832
|
var ANONYMOUS_KEY = "anonymous";
|
|
4834
4833
|
var RUN_KEY = { envVar: "OPENPOUCH_API_KEY", file: "openpouch-run.key" };
|
|
4834
|
+
function runKeyPath(env) {
|
|
4835
|
+
const override = env["OPENPOUCH_KEY_FILE"]?.trim();
|
|
4836
|
+
if (override) return override;
|
|
4837
|
+
const home = env["OPENPOUCH_HOME"] ?? homedir();
|
|
4838
|
+
return join2(home, ".openpouch", RUN_KEY.file);
|
|
4839
|
+
}
|
|
4835
4840
|
async function resolveRunKey(env) {
|
|
4836
4841
|
const fromEnv = env[RUN_KEY.envVar]?.trim();
|
|
4837
4842
|
if (fromEnv) return fromEnv;
|
|
4838
4843
|
try {
|
|
4839
|
-
const
|
|
4840
|
-
const fromFile = (await readFile2(join2(home, ".openpouch", RUN_KEY.file), "utf8")).trim();
|
|
4844
|
+
const fromFile = (await readFile2(runKeyPath(env), "utf8")).trim();
|
|
4841
4845
|
if (fromFile.length > 0) return fromFile;
|
|
4842
4846
|
} catch {
|
|
4843
4847
|
}
|
|
@@ -4893,6 +4897,15 @@ function keyPublicPrefix(key) {
|
|
|
4893
4897
|
const m = /^(op_live_[0-9a-f]+)_/.exec(key);
|
|
4894
4898
|
return m ? m[1] : "op_live_\u2026";
|
|
4895
4899
|
}
|
|
4900
|
+
function looksLikeOpenpouchKey(content) {
|
|
4901
|
+
const t = content.trim();
|
|
4902
|
+
if (t.length === 0) return true;
|
|
4903
|
+
if (t.includes("\n")) return false;
|
|
4904
|
+
return /^op_(?:live|test)_[0-9a-f]{6,}_[A-Za-z0-9_-]+$/.test(t);
|
|
4905
|
+
}
|
|
4906
|
+
function looksLikePrivateKey(content) {
|
|
4907
|
+
return /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/.test(content);
|
|
4908
|
+
}
|
|
4896
4909
|
async function activateCommand(ctx, opts) {
|
|
4897
4910
|
const account = opts.account?.trim();
|
|
4898
4911
|
const token = opts.token?.trim();
|
|
@@ -4911,17 +4924,29 @@ async function activateCommand(ctx, opts) {
|
|
|
4911
4924
|
try {
|
|
4912
4925
|
const res = await adapter.verifyEmail(account, token);
|
|
4913
4926
|
let saved = false;
|
|
4914
|
-
let
|
|
4927
|
+
let reason;
|
|
4928
|
+
const keyPath = opts.keyFile?.trim() || runKeyPath(ctx.env);
|
|
4915
4929
|
try {
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4930
|
+
let existing = null;
|
|
4931
|
+
try {
|
|
4932
|
+
existing = await readFile3(keyPath, "utf8");
|
|
4933
|
+
} catch (e) {
|
|
4934
|
+
if (e.code !== "ENOENT") throw e;
|
|
4935
|
+
}
|
|
4936
|
+
if (existing !== null && existing.trim().length > 0 && !looksLikeOpenpouchKey(existing)) {
|
|
4937
|
+
const how = "Set OPENPOUCH_KEY_FILE (or pass --key-file) to a free path, or move that file.";
|
|
4938
|
+
reason = looksLikePrivateKey(existing) ? `refusing to overwrite ${keyPath} \u2014 it looks like an existing private key. ${how}` : `refusing to overwrite ${keyPath} \u2014 it is not an openpouch key file. ${how}`;
|
|
4939
|
+
} else {
|
|
4940
|
+
await mkdir(dirname(keyPath), { recursive: true });
|
|
4941
|
+
if (existing !== null && existing.trim().length > 0) {
|
|
4942
|
+
await writeFile3(`${keyPath}.bak`, existing, { mode: 384 });
|
|
4943
|
+
}
|
|
4944
|
+
await writeFile3(keyPath, `${res.key}
|
|
4921
4945
|
`, { mode: 384 });
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4946
|
+
saved = true;
|
|
4947
|
+
}
|
|
4948
|
+
} catch (e) {
|
|
4949
|
+
reason = `could not save the key to ${keyPath}: ${e.message}`;
|
|
4925
4950
|
}
|
|
4926
4951
|
return summarized(
|
|
4927
4952
|
EXIT.OK,
|
|
@@ -4930,12 +4955,12 @@ async function activateCommand(ctx, opts) {
|
|
|
4930
4955
|
account: res.account,
|
|
4931
4956
|
keyPrefix: keyPublicPrefix(res.key),
|
|
4932
4957
|
saved,
|
|
4933
|
-
...saved ? { keyPath } : { key: res.key }
|
|
4958
|
+
...saved ? { keyPath } : { reason, key: res.key }
|
|
4934
4959
|
},
|
|
4935
4960
|
activateSummary(saved),
|
|
4936
4961
|
[
|
|
4937
4962
|
`openpouch activate \u2713 ${res.account.id} [${res.account.tier}]`,
|
|
4938
|
-
saved ? ` saved your key to ${keyPath} (chmod 600) \u2014 deploys now run under your account` : ` \u26A0
|
|
4963
|
+
...saved ? [` saved your key to ${keyPath} (chmod 600) \u2014 deploys now run under your account`] : [` \u26A0 key NOT saved: ${reason}`, ` \u2192 use it now: export OPENPOUCH_API_KEY=${res.key}`],
|
|
4939
4964
|
` key: ${keyPublicPrefix(res.key)}\u2026`
|
|
4940
4965
|
]
|
|
4941
4966
|
);
|
|
@@ -4952,33 +4977,33 @@ import { createInterface } from "node:readline/promises";
|
|
|
4952
4977
|
|
|
4953
4978
|
// packages/cli/src/approvals.ts
|
|
4954
4979
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
4955
|
-
import { mkdir as mkdir2, readFile as
|
|
4956
|
-
import { homedir as
|
|
4957
|
-
import { join as
|
|
4980
|
+
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
4981
|
+
import { homedir as homedir2, userInfo } from "node:os";
|
|
4982
|
+
import { join as join3 } from "node:path";
|
|
4958
4983
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4959
4984
|
async function loadApprovals(cwd) {
|
|
4960
4985
|
try {
|
|
4961
|
-
const raw = await
|
|
4986
|
+
const raw = await readFile4(join3(cwd, APPROVALS_FILENAME), "utf8");
|
|
4962
4987
|
return approvalsFileSchema.parse(JSON.parse(raw));
|
|
4963
4988
|
} catch {
|
|
4964
4989
|
return { version: 0, requests: [] };
|
|
4965
4990
|
}
|
|
4966
4991
|
}
|
|
4967
4992
|
async function saveApprovals(cwd, file) {
|
|
4968
|
-
await mkdir2(
|
|
4993
|
+
await mkdir2(join3(cwd, APPROVALS_DIR), { recursive: true });
|
|
4969
4994
|
await ensureGitignored(cwd);
|
|
4970
4995
|
await writeFile4(
|
|
4971
|
-
|
|
4996
|
+
join3(cwd, APPROVALS_FILENAME),
|
|
4972
4997
|
`${JSON.stringify(approvalsFileSchema.parse(file), null, 2)}
|
|
4973
4998
|
`,
|
|
4974
4999
|
"utf8"
|
|
4975
5000
|
);
|
|
4976
5001
|
}
|
|
4977
5002
|
async function ensureGitignored(cwd) {
|
|
4978
|
-
const path =
|
|
5003
|
+
const path = join3(cwd, ".gitignore");
|
|
4979
5004
|
let content = "";
|
|
4980
5005
|
try {
|
|
4981
|
-
content = await
|
|
5006
|
+
content = await readFile4(path, "utf8");
|
|
4982
5007
|
} catch {
|
|
4983
5008
|
}
|
|
4984
5009
|
if (!content.split("\n").some((line) => line.trim() === ".openpouch/")) {
|
|
@@ -4999,10 +5024,10 @@ function newApprovalRequest(match, now) {
|
|
|
4999
5024
|
};
|
|
5000
5025
|
}
|
|
5001
5026
|
async function getApproverSecret(createIfMissing, home) {
|
|
5002
|
-
const dir =
|
|
5003
|
-
const path =
|
|
5027
|
+
const dir = join3(home ?? homedir2(), ".openpouch");
|
|
5028
|
+
const path = join3(dir, "approver.secret");
|
|
5004
5029
|
try {
|
|
5005
|
-
const existing = (await
|
|
5030
|
+
const existing = (await readFile4(path, "utf8")).trim();
|
|
5006
5031
|
if (existing.length > 0) return existing;
|
|
5007
5032
|
} catch {
|
|
5008
5033
|
}
|
|
@@ -5322,11 +5347,11 @@ async function deployCommand(ctx, environment) {
|
|
|
5322
5347
|
}
|
|
5323
5348
|
|
|
5324
5349
|
// packages/cli/src/commands/init.ts
|
|
5325
|
-
import { join as
|
|
5350
|
+
import { join as join6 } from "node:path";
|
|
5326
5351
|
|
|
5327
5352
|
// packages/cli/src/agentsmd.ts
|
|
5328
|
-
import { readFile as
|
|
5329
|
-
import { join as
|
|
5353
|
+
import { readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
|
|
5354
|
+
import { join as join4 } from "node:path";
|
|
5330
5355
|
var AGENTS_MD_FILENAME = "AGENTS.md";
|
|
5331
5356
|
var START = "<!-- openpouch:start -->";
|
|
5332
5357
|
var END = "<!-- openpouch:end -->";
|
|
@@ -5349,11 +5374,11 @@ function renderAgentsSection(projectName) {
|
|
|
5349
5374
|
].join("\n");
|
|
5350
5375
|
}
|
|
5351
5376
|
async function upsertAgentsSection(cwd, projectName) {
|
|
5352
|
-
const path =
|
|
5377
|
+
const path = join4(cwd, AGENTS_MD_FILENAME);
|
|
5353
5378
|
const section = renderAgentsSection(projectName);
|
|
5354
5379
|
let existing;
|
|
5355
5380
|
try {
|
|
5356
|
-
existing = await
|
|
5381
|
+
existing = await readFile5(path, "utf8");
|
|
5357
5382
|
} catch {
|
|
5358
5383
|
existing = void 0;
|
|
5359
5384
|
}
|
|
@@ -5380,8 +5405,8 @@ ${section}
|
|
|
5380
5405
|
}
|
|
5381
5406
|
|
|
5382
5407
|
// packages/cli/src/detect.ts
|
|
5383
|
-
import { readFile as
|
|
5384
|
-
import { basename, extname, join as
|
|
5408
|
+
import { readFile as readFile6, readdir } from "node:fs/promises";
|
|
5409
|
+
import { basename, extname, join as join5 } from "node:path";
|
|
5385
5410
|
var FRAMEWORK_BY_DEP = [
|
|
5386
5411
|
["next", "nextjs"],
|
|
5387
5412
|
["astro", "astro"],
|
|
@@ -5416,7 +5441,7 @@ async function collectSourceFiles(root) {
|
|
|
5416
5441
|
}
|
|
5417
5442
|
for (const entry of entries) {
|
|
5418
5443
|
if (files.length >= MAX_FILES) break;
|
|
5419
|
-
const full =
|
|
5444
|
+
const full = join5(current.dir, entry.name);
|
|
5420
5445
|
if (entry.isDirectory()) {
|
|
5421
5446
|
if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".") && current.depth < MAX_DEPTH) {
|
|
5422
5447
|
stack.push({ dir: full, depth: current.depth + 1 });
|
|
@@ -5433,7 +5458,7 @@ async function scanEnvVarNames(root) {
|
|
|
5433
5458
|
for (const file of await collectSourceFiles(root)) {
|
|
5434
5459
|
let content;
|
|
5435
5460
|
try {
|
|
5436
|
-
const buf = await
|
|
5461
|
+
const buf = await readFile6(file);
|
|
5437
5462
|
if (buf.byteLength > MAX_FILE_BYTES) continue;
|
|
5438
5463
|
content = buf.toString("utf8");
|
|
5439
5464
|
} catch {
|
|
@@ -5451,7 +5476,7 @@ async function scanEnvVarNames(root) {
|
|
|
5451
5476
|
async function detectProject(cwd) {
|
|
5452
5477
|
let pkg = {};
|
|
5453
5478
|
try {
|
|
5454
|
-
pkg = JSON.parse(await
|
|
5479
|
+
pkg = JSON.parse(await readFile6(join5(cwd, "package.json"), "utf8"));
|
|
5455
5480
|
} catch {
|
|
5456
5481
|
}
|
|
5457
5482
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
@@ -5561,8 +5586,8 @@ async function initCommand(ctx, flags) {
|
|
|
5561
5586
|
"This is an openpouch bug \u2014 please report it."
|
|
5562
5587
|
);
|
|
5563
5588
|
}
|
|
5564
|
-
await writeJsonFile(
|
|
5565
|
-
await writeJsonFile(
|
|
5589
|
+
await writeJsonFile(join6(ctx.cwd, MANIFEST_FILENAME), validated.value);
|
|
5590
|
+
await writeJsonFile(join6(ctx.cwd, POLICY_FILENAME), defaultPolicy());
|
|
5566
5591
|
const agentsMd = await upsertAgentsSection(ctx.cwd, detected.name);
|
|
5567
5592
|
hints.push("policy default: previews autonomous, production requires approval \u2014 edit deploy.policy.json to change");
|
|
5568
5593
|
return summarized(
|
|
@@ -5602,8 +5627,8 @@ async function initCommand(ctx, flags) {
|
|
|
5602
5627
|
|
|
5603
5628
|
// packages/cli/src/commands/instant.ts
|
|
5604
5629
|
import { spawn } from "node:child_process";
|
|
5605
|
-
import { readFile as
|
|
5606
|
-
import { basename as basename2, join as
|
|
5630
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
5631
|
+
import { basename as basename2, join as join7 } from "node:path";
|
|
5607
5632
|
var TAR_EXCLUDES = [
|
|
5608
5633
|
"./node_modules",
|
|
5609
5634
|
"./.git",
|
|
@@ -5632,7 +5657,7 @@ function buildTarball(cwd) {
|
|
|
5632
5657
|
}
|
|
5633
5658
|
async function projectHint(cwd) {
|
|
5634
5659
|
try {
|
|
5635
|
-
const pkg = JSON.parse(await
|
|
5660
|
+
const pkg = JSON.parse(await readFile7(join7(cwd, "package.json"), "utf8"));
|
|
5636
5661
|
if (pkg.name) return pkg.name.replace(/^@[^/]+\//, "");
|
|
5637
5662
|
} catch {
|
|
5638
5663
|
}
|
|
@@ -5641,12 +5666,16 @@ async function projectHint(cwd) {
|
|
|
5641
5666
|
async function instantCommand(ctx) {
|
|
5642
5667
|
const loaded = await loadManifest(ctx.cwd);
|
|
5643
5668
|
if (loaded.status === "ok") {
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5669
|
+
const envs = Object.values(loaded.manifest.environments ?? {});
|
|
5670
|
+
const hasGovernedEnv = envs.some((e) => e.provider !== "openpouch-run");
|
|
5671
|
+
if (hasGovernedEnv) {
|
|
5672
|
+
return errorResult(
|
|
5673
|
+
EXIT.USAGE,
|
|
5674
|
+
"usage",
|
|
5675
|
+
"this project already has deploy.manifest.json",
|
|
5676
|
+
"Use `openpouch preview` / `openpouch prod` for a configured project. `openpouch deploy` is the zero-config instant lane."
|
|
5677
|
+
);
|
|
5678
|
+
}
|
|
5650
5679
|
}
|
|
5651
5680
|
let tarball;
|
|
5652
5681
|
try {
|
|
@@ -5673,8 +5702,8 @@ async function instantCommand(ctx) {
|
|
|
5673
5702
|
preview: { provider: "openpouch-run", serviceId: result2.slug, url: result2.url }
|
|
5674
5703
|
}
|
|
5675
5704
|
};
|
|
5676
|
-
await writeJsonFile(
|
|
5677
|
-
await writeJsonFile(
|
|
5705
|
+
await writeJsonFile(join7(ctx.cwd, MANIFEST_FILENAME), manifest);
|
|
5706
|
+
await writeJsonFile(join7(ctx.cwd, POLICY_FILENAME), defaultPolicy());
|
|
5678
5707
|
await appendDeployment(ctx.cwd, {
|
|
5679
5708
|
id: result2.slug,
|
|
5680
5709
|
environment: "preview",
|
|
@@ -6216,8 +6245,9 @@ var USAGE = [
|
|
|
6216
6245
|
" --github (signup) create an account via GitHub (prints the authorize URL)",
|
|
6217
6246
|
" --account <id> (activate) the account id from signup",
|
|
6218
6247
|
" --token <tok> (activate) the verification token from the signup email",
|
|
6248
|
+
" --key-file <p> (activate) where to save the issued key (default ~/.openpouch/openpouch-run.key; never overwrites a non-openpouch file)",
|
|
6219
6249
|
"",
|
|
6220
|
-
"account key: deploys send Authorization: Bearer from OPENPOUCH_API_KEY or ~/.openpouch/openpouch-run.key (optional \u2014 no key = anonymous)",
|
|
6250
|
+
"account key: deploys send Authorization: Bearer from OPENPOUCH_API_KEY or ~/.openpouch/openpouch-run.key (override the path with OPENPOUCH_KEY_FILE; optional \u2014 no key = anonymous)",
|
|
6221
6251
|
"exit codes: 0 ok \xB7 1 unexpected \xB7 2 usage \xB7 3 manifest/policy missing or invalid \xB7 4 auth \xB7 5 provider \xB7 6 policy gate (approval required/denied/human required) \xB7 7 deploy failed \xB7 8 verify failed"
|
|
6222
6252
|
].join("\n");
|
|
6223
6253
|
function render(result2, json) {
|
|
@@ -6240,7 +6270,8 @@ async function run(argv, ctx) {
|
|
|
6240
6270
|
limit: { type: "string" },
|
|
6241
6271
|
email: { type: "string" },
|
|
6242
6272
|
account: { type: "string" },
|
|
6243
|
-
token: { type: "string" }
|
|
6273
|
+
token: { type: "string" },
|
|
6274
|
+
"key-file": { type: "string" }
|
|
6244
6275
|
},
|
|
6245
6276
|
allowPositionals: true
|
|
6246
6277
|
});
|
|
@@ -6269,7 +6300,14 @@ async function run(argv, ctx) {
|
|
|
6269
6300
|
case "signup":
|
|
6270
6301
|
return render(await signupCommand(ctx, { email: parsed.values.email, github: parsed.values.github === true }), json);
|
|
6271
6302
|
case "activate":
|
|
6272
|
-
return render(
|
|
6303
|
+
return render(
|
|
6304
|
+
await activateCommand(ctx, {
|
|
6305
|
+
account: parsed.values.account,
|
|
6306
|
+
token: parsed.values.token,
|
|
6307
|
+
keyFile: parsed.values["key-file"]
|
|
6308
|
+
}),
|
|
6309
|
+
json
|
|
6310
|
+
);
|
|
6273
6311
|
case "whoami":
|
|
6274
6312
|
return render(await whoamiCommand(ctx), json);
|
|
6275
6313
|
case "init":
|
package/package.json
CHANGED