artifactpass 0.1.0-rc.2 → 0.1.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -14821,6 +14821,33 @@ import { spawn } from "node:child_process";
14821
14821
  var ARTIFACTPASS_CREDENTIAL_SERVICE = "artifactpass";
14822
14822
  var LEGACY_ARTIFACT_SHARE_CREDENTIAL_SERVICE = "lordebuilds.artifacts.share";
14823
14823
  var agentCredentialAccountForProfile = (profileName) => profileName === "production" ? "agent-token" : `agent-token:${profileName}`;
14824
+ var agentTokenPattern = /^as_[A-Za-z0-9_-]{43}$/u;
14825
+ var bindAgentCredential = (originValue, token) => {
14826
+ if (!agentTokenPattern.test(token)) throw new Error("ArtifactPass agent credential is malformed");
14827
+ return JSON.stringify({ version: 1, origin: new URL(originValue).origin, token });
14828
+ };
14829
+ var resolveAgentCredential = (storedValue, expectedOriginValue, requireOriginBinding = false) => {
14830
+ if (agentTokenPattern.test(storedValue)) {
14831
+ if (requireOriginBinding) {
14832
+ throw new Error("ArtifactPass credential predates origin binding; reconnect this profile before publishing");
14833
+ }
14834
+ return storedValue;
14835
+ }
14836
+ let parsed;
14837
+ try {
14838
+ parsed = JSON.parse(storedValue);
14839
+ } catch {
14840
+ throw new Error("ArtifactPass agent credential is malformed");
14841
+ }
14842
+ if (parsed === null || typeof parsed !== "object" || parsed.version !== 1 || typeof parsed.origin !== "string" || typeof parsed.token !== "string" || !agentTokenPattern.test(parsed.token)) {
14843
+ throw new Error("ArtifactPass agent credential is malformed");
14844
+ }
14845
+ const expectedOrigin = new URL(expectedOriginValue).origin;
14846
+ if (new URL(parsed.origin).origin !== expectedOrigin) {
14847
+ throw new Error("ArtifactPass credential belongs to a different deployment; reconnect this profile");
14848
+ }
14849
+ return parsed.token;
14850
+ };
14824
14851
  var CredentialStoreCommandError = class extends Error {
14825
14852
  constructor(status) {
14826
14853
  super(`Credential store command failed with status ${status ?? "unknown"}`);
@@ -14987,6 +15014,9 @@ var validateProfile = (value2) => {
14987
15014
  if (candidate.credential_namespace !== void 0 && candidate.credential_namespace !== "artifactpass") {
14988
15015
  throw new Error("ArtifactPass config contains an invalid credential namespace");
14989
15016
  }
15017
+ if (candidate.credential_binding !== void 0 && candidate.credential_binding !== "origin") {
15018
+ throw new Error("ArtifactPass config contains an invalid credential binding");
15019
+ }
14990
15020
  if (candidate.open_development !== void 0 && candidate.open_development !== true) {
14991
15021
  throw new Error("ArtifactPass config open_development must be true when enabled");
14992
15022
  }
@@ -15006,7 +15036,8 @@ var validateProfile = (value2) => {
15006
15036
  ...typeof candidate.pdf_key_id === "string" ? { pdf_key_id: candidate.pdf_key_id } : {},
15007
15037
  ...candidate.publication_state === "legacy" ? { publication_state: "legacy" } : {},
15008
15038
  ...typeof candidate.publication_state_path === "string" ? { publication_state_path: candidate.publication_state_path } : {},
15009
- ...candidate.credential_namespace === "artifactpass" ? { credential_namespace: "artifactpass" } : {}
15039
+ ...candidate.credential_namespace === "artifactpass" ? { credential_namespace: "artifactpass" } : {},
15040
+ ...candidate.credential_binding === "origin" ? { credential_binding: "origin" } : {}
15010
15041
  };
15011
15042
  };
15012
15043
  var validateSettings = (value2) => {
@@ -15108,10 +15139,10 @@ import { DatabaseSync } from "node:sqlite";
15108
15139
 
15109
15140
  // ../agent-bridge/src/logging/redacting-logger.ts
15110
15141
  var bearerPattern = /\bBearer\s+[A-Za-z0-9._~-]+/giu;
15111
- var agentTokenPattern = /\bas_[A-Za-z0-9_-]{20,}\b/gu;
15142
+ var agentTokenPattern2 = /\bas_[A-Za-z0-9_-]{20,}\b/gu;
15112
15143
  var cloudflareTokenPattern = /\bcfut_[A-Za-z0-9_-]{20,}\b/gu;
15113
15144
  var shareUrlPattern = /https:\/\/[^\s/]+\/a\/[A-Za-z0-9_-]{32,256}(?:\/[^\s]*)?/gu;
15114
- var redactSensitiveText = (value2) => value2.replace(bearerPattern, "Bearer [REDACTED]").replace(agentTokenPattern, "[REDACTED]").replace(cloudflareTokenPattern, "[REDACTED]").replace(shareUrlPattern, "[REDACTED SHARE URL]");
15145
+ var redactSensitiveText = (value2) => value2.replace(bearerPattern, "Bearer [REDACTED]").replace(agentTokenPattern2, "[REDACTED]").replace(cloudflareTokenPattern, "[REDACTED]").replace(shareUrlPattern, "[REDACTED SHARE URL]");
15115
15146
 
15116
15147
  // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.0.0/node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
15117
15148
  var __create = Object.create;
@@ -28268,7 +28299,7 @@ var publishArtifactInputSchema = external_exports.object({
28268
28299
  "Optional UTF-8 source used to generate a PDF. When configured, ArtifactPass verifies it against the PDF and signs the agent-readable representation."
28269
28300
  ),
28270
28301
  expires_in_seconds: external_exports.number().int().positive().default(3600).describe(
28271
- "Deployment expiry preset in seconds. Defaults to one hour (3600). Default setup presets: 900, 1800, 3600, 43200, 86400; a rejection reports the deployment's allowed values."
28302
+ "Deployment expiry preset in seconds. Defaults to one hour (3600). Public ArtifactPass presets: 900, 1800, or 3600; a rejection reports the deployment's allowed values."
28272
28303
  )
28273
28304
  });
28274
28305
  var publishArtifactOutputSchema = external_exports.object({
@@ -28279,7 +28310,9 @@ var publishArtifactOutputSchema = external_exports.object({
28279
28310
  var readArtifactInputSchema = external_exports.object({
28280
28311
  share_url: external_exports.string().url(),
28281
28312
  cursor: external_exports.string().optional(),
28282
- max_bytes: external_exports.number().int().positive().optional(),
28313
+ max_bytes: external_exports.number().int().positive().max(PROTOCOL_MAX_SOURCE_CHUNK_BYTES).optional().describe(
28314
+ `Maximum source bytes per call. Defaults to ${PROTOCOL_MAX_SOURCE_CHUNK_BYTES}.`
28315
+ ),
28283
28316
  representation: external_exports.enum(["auto", "source", "derived"]).optional()
28284
28317
  });
28285
28318
  var readArtifactOutputSchema = external_exports.object({
@@ -28454,11 +28487,34 @@ var deploymentPlan = (input) => {
28454
28487
  if (!workersSubdomainPattern.test(input.workersSubdomain)) {
28455
28488
  throw new Error("Choose a valid Workers account subdomain");
28456
28489
  }
28457
- if (input.identities.length === 0) throw new Error("At least one allowed identity is required");
28490
+ if (input.publicAuth === void 0 && input.identities.length === 0) {
28491
+ throw new Error("At least one allowed identity is required");
28492
+ }
28493
+ if (input.publicAuth !== void 0 && input.identities.length > 0) {
28494
+ throw new Error("Public ArtifactPass authentication cannot include Cloudflare Access identities");
28495
+ }
28458
28496
  for (const identity of input.identities) {
28459
28497
  const valid = identity.kind === "email" ? /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/u.test(identity.value) : hostnamePattern.test(`x.${identity.value}`);
28460
28498
  if (!valid) throw new Error(`Allowed ${identity.kind} is invalid`);
28461
28499
  }
28500
+ if (input.publicAuth !== void 0) {
28501
+ for (const [name, credential] of Object.entries(input.publicAuth)) {
28502
+ if (credential.trim().length === 0 || credential.length > 512) {
28503
+ throw new Error(`${name} must be a non-empty OAuth credential`);
28504
+ }
28505
+ }
28506
+ }
28507
+ if (input.publicAuth !== void 0) {
28508
+ return [
28509
+ "verify the short-lived Cloudflare API token, selected zone, and Workers account subdomain",
28510
+ "reuse or create the private D1 database and R2 bucket",
28511
+ "prepare the public Worker configuration with Google and GitHub OAuth secrets",
28512
+ "apply D1 migrations and the R2 cleanup lifecycle",
28513
+ "deploy the Worker and verify ArtifactPass authentication before removing the old Access gate",
28514
+ "verify public sign-in, protected upload redirection, and health",
28515
+ "print the public connection command without persisting provisioning credentials"
28516
+ ];
28517
+ }
28462
28518
  return [
28463
28519
  "verify the short-lived Cloudflare API token, selected zone, and Workers account subdomain",
28464
28520
  "reuse or create the private D1 database and R2 bucket",
@@ -28475,6 +28531,34 @@ var expectedDestinations = (hostname3) => [
28475
28531
  ];
28476
28532
  var identityIncludes = (identities) => identities.map((identity) => identity.kind === "email" ? { email: { email: identity.value } } : { email_domain: { domain: identity.value } });
28477
28533
  var canonicalJson = (value2) => JSON.stringify([...value2].sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))));
28534
+ var restoreLegacyAccess = async (input, serviceName, policies, dependencies) => {
28535
+ const restored = await dependencies.client.request(
28536
+ `/accounts/${input.accountId}/access/apps`,
28537
+ {
28538
+ method: "POST",
28539
+ body: JSON.stringify({
28540
+ name: serviceName,
28541
+ type: "self_hosted",
28542
+ session_duration: "24h",
28543
+ destinations: expectedDestinations(input.hostname)
28544
+ })
28545
+ }
28546
+ );
28547
+ for (const policy of policies) {
28548
+ await dependencies.client.request(
28549
+ `/accounts/${input.accountId}/access/apps/${restored.id}/policies`,
28550
+ {
28551
+ method: "POST",
28552
+ body: JSON.stringify({
28553
+ name: policy.name,
28554
+ decision: policy.decision ?? "allow",
28555
+ include: policy.include ?? []
28556
+ })
28557
+ }
28558
+ );
28559
+ }
28560
+ return restored;
28561
+ };
28478
28562
  var deploymentFiles = async (root, directory = root) => {
28479
28563
  const entries = await readdir(directory);
28480
28564
  const files = [];
@@ -28545,7 +28629,14 @@ var approvalBinding = async (input, serviceName, dependencies) => {
28545
28629
  serviceName,
28546
28630
  pdfKeyId: input.pdfKeyId,
28547
28631
  pdfPublicKeySha256: createHash("sha256").update(input.pdfPublicKey).digest("hex"),
28548
- workersSubdomain: input.workersSubdomain
28632
+ workersSubdomain: input.workersSubdomain,
28633
+ authMode: input.publicAuth === void 0 ? "cloudflare-access" : "artifactpass",
28634
+ ...input.publicAuth === void 0 ? {} : {
28635
+ googleClientId: input.publicAuth.googleClientId,
28636
+ googleClientSecretSha256: createHash("sha256").update(input.publicAuth.googleClientSecret).digest("hex"),
28637
+ githubClientId: input.publicAuth.githubClientId,
28638
+ githubClientSecretSha256: createHash("sha256").update(input.publicAuth.githubClientSecret).digest("hex")
28639
+ }
28549
28640
  },
28550
28641
  bundleSha256: await deploymentSha256(dependencies.deploymentRoot),
28551
28642
  remote: {
@@ -28564,7 +28655,7 @@ var approvalBinding = async (input, serviceName, dependencies) => {
28564
28655
  };
28565
28656
  };
28566
28657
  var readinessRetryDelays = [1e3, 2e3, 4e3, 8e3, 15e3];
28567
- var fetchAfterDeploymentPropagation = async (fetchImplementation, request, init, sleep2, timeoutMilliseconds) => {
28658
+ var fetchAfterDeploymentPropagation = async (fetchImplementation, request, init, sleep2, timeoutMilliseconds, isReady = (response) => response.status !== 404 && response.status !== 429 && response.status < 500) => {
28568
28659
  let lastError;
28569
28660
  for (let attempt = 0; attempt <= readinessRetryDelays.length; attempt += 1) {
28570
28661
  try {
@@ -28572,7 +28663,7 @@ var fetchAfterDeploymentPropagation = async (fetchImplementation, request, init,
28572
28663
  ...init,
28573
28664
  signal: AbortSignal.timeout(timeoutMilliseconds)
28574
28665
  });
28575
- if (response.status !== 404 && response.status !== 429 && response.status < 500) return response;
28666
+ if (await isReady(response.clone())) return response;
28576
28667
  lastError = new Error(`Deployment route is not ready (${response.status})`);
28577
28668
  } catch (error51) {
28578
28669
  lastError = error51;
@@ -28583,6 +28674,17 @@ var fetchAfterDeploymentPropagation = async (fetchImplementation, request, init,
28583
28674
  }
28584
28675
  throw lastError;
28585
28676
  };
28677
+ var isArtifactPassUploadRedirect = (response, baseUrl) => {
28678
+ if (response.status !== 302) return false;
28679
+ const location = response.headers.get("location");
28680
+ if (location === null) return false;
28681
+ try {
28682
+ const signInRedirect = new URL(location, baseUrl);
28683
+ return signInRedirect.origin === baseUrl && signInRedirect.pathname === "/auth/sign-in" && signInRedirect.searchParams.get("return_to") === "/upload";
28684
+ } catch {
28685
+ return false;
28686
+ }
28687
+ };
28586
28688
  var deployArtifactShare = async (input, dependencies) => {
28587
28689
  const plan = deploymentPlan(input);
28588
28690
  const serviceName = input.serviceName ?? "lordebuilds-artifacts-share";
@@ -28597,7 +28699,7 @@ var deployArtifactShare = async (input, dependencies) => {
28597
28699
  const binding = await approvalBinding(input, serviceName, dependencies);
28598
28700
  if (input.writeApprovalManifest !== void 0) {
28599
28701
  const manifest = {
28600
- version: 1,
28702
+ version: 2,
28601
28703
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
28602
28704
  binding
28603
28705
  };
@@ -28612,7 +28714,7 @@ var deployArtifactShare = async (input, dependencies) => {
28612
28714
  };
28613
28715
  }
28614
28716
  const approved = JSON.parse(await readFile2(input.approveManifest ?? "", "utf8"));
28615
- if (approved.version !== 1 || JSON.stringify(approved.binding) !== JSON.stringify(binding)) {
28717
+ if (approved.version !== 2 || JSON.stringify(approved.binding) !== JSON.stringify(binding)) {
28616
28718
  throw new Error("Hosted approval manifest no longer matches the deployment bundle or Cloudflare state");
28617
28719
  }
28618
28720
  }
@@ -28666,7 +28768,18 @@ var deployArtifactShare = async (input, dependencies) => {
28666
28768
  `/accounts/${input.accountId}/access/apps`
28667
28769
  );
28668
28770
  let application = applications.find((candidate) => candidate.name === serviceName);
28669
- if (application === void 0) {
28771
+ const legacyAccessPolicies = input.publicAuth !== void 0 && application !== void 0 ? await dependencies.client.request(
28772
+ `/accounts/${input.accountId}/access/apps/${application.id}/policies`
28773
+ ) : [];
28774
+ if (input.publicAuth !== void 0 && application !== void 0) {
28775
+ const actual = canonicalJson((application.destinations ?? []).map((destination) => ({
28776
+ type: destination.type,
28777
+ ...destination.uri === void 0 ? {} : { uri: destination.uri }
28778
+ })));
28779
+ if (actual !== canonicalJson(expectedDestinations(input.hostname))) {
28780
+ throw new Error("Existing Access application has different protected paths and cannot be removed safely");
28781
+ }
28782
+ } else if (input.publicAuth === void 0 && application === void 0) {
28670
28783
  application = await dependencies.client.request(
28671
28784
  `/accounts/${input.accountId}/access/apps`,
28672
28785
  {
@@ -28680,7 +28793,8 @@ var deployArtifactShare = async (input, dependencies) => {
28680
28793
  }
28681
28794
  );
28682
28795
  changed.push("Access application");
28683
- } else {
28796
+ } else if (input.publicAuth === void 0) {
28797
+ if (application === void 0) throw new Error("Cloudflare Access application is unavailable");
28684
28798
  const actual = canonicalJson((application.destinations ?? []).map((destination) => ({
28685
28799
  type: destination.type,
28686
28800
  ...destination.uri === void 0 ? {} : { uri: destination.uri }
@@ -28689,43 +28803,46 @@ var deployArtifactShare = async (input, dependencies) => {
28689
28803
  throw new Error("Existing Access application has different protected paths");
28690
28804
  }
28691
28805
  }
28692
- if (typeof application.aud !== "string" || application.aud.length === 0) {
28693
- throw new Error("Cloudflare Access application did not return an audience tag");
28694
- }
28695
- if (!/^[a-z0-9-]+\.cloudflareaccess\.com$/u.test(organization.auth_domain)) {
28696
- throw new Error("Cloudflare Access organization returned an invalid team domain");
28697
- }
28698
- const policies = await dependencies.client.request(
28699
- `/accounts/${input.accountId}/access/apps/${application.id}/policies`
28700
- );
28701
- const expectedIncludes = identityIncludes(input.identities);
28702
- const policy = policies.find((candidate) => candidate.name === "Artifact Share uploaders");
28703
- if (policy === void 0) {
28704
- await dependencies.client.request(
28705
- `/accounts/${input.accountId}/access/apps/${application.id}/policies`,
28706
- {
28707
- method: "POST",
28708
- body: JSON.stringify({
28709
- name: "Artifact Share uploaders",
28710
- decision: "allow",
28711
- include: expectedIncludes
28712
- })
28713
- }
28714
- );
28715
- changed.push("Access policy");
28716
- } else if (policy.decision !== "allow" || canonicalJson(policy.include ?? []) !== canonicalJson(expectedIncludes)) {
28717
- await dependencies.client.request(
28718
- `/accounts/${input.accountId}/access/apps/${application.id}/policies/${policy.id}`,
28719
- {
28720
- method: "PUT",
28721
- body: JSON.stringify({
28722
- name: "Artifact Share uploaders",
28723
- decision: "allow",
28724
- include: expectedIncludes
28725
- })
28726
- }
28806
+ if (input.publicAuth === void 0) {
28807
+ if (application === void 0) throw new Error("Cloudflare Access application is unavailable");
28808
+ if (typeof application.aud !== "string" || application.aud.length === 0) {
28809
+ throw new Error("Cloudflare Access application did not return an audience tag");
28810
+ }
28811
+ if (!/^[a-z0-9-]+\.cloudflareaccess\.com$/u.test(organization.auth_domain)) {
28812
+ throw new Error("Cloudflare Access organization returned an invalid team domain");
28813
+ }
28814
+ const policies = await dependencies.client.request(
28815
+ `/accounts/${input.accountId}/access/apps/${application.id}/policies`
28727
28816
  );
28728
- changed.push("Access policy");
28817
+ const expectedIncludes = identityIncludes(input.identities);
28818
+ const policy = policies.find((candidate) => candidate.name === "Artifact Share uploaders");
28819
+ if (policy === void 0) {
28820
+ await dependencies.client.request(
28821
+ `/accounts/${input.accountId}/access/apps/${application.id}/policies`,
28822
+ {
28823
+ method: "POST",
28824
+ body: JSON.stringify({
28825
+ name: "Artifact Share uploaders",
28826
+ decision: "allow",
28827
+ include: expectedIncludes
28828
+ })
28829
+ }
28830
+ );
28831
+ changed.push("Access policy");
28832
+ } else if (policy.decision !== "allow" || canonicalJson(policy.include ?? []) !== canonicalJson(expectedIncludes)) {
28833
+ await dependencies.client.request(
28834
+ `/accounts/${input.accountId}/access/apps/${application.id}/policies/${policy.id}`,
28835
+ {
28836
+ method: "PUT",
28837
+ body: JSON.stringify({
28838
+ name: "Artifact Share uploaders",
28839
+ decision: "allow",
28840
+ include: expectedIncludes
28841
+ })
28842
+ }
28843
+ );
28844
+ changed.push("Access policy");
28845
+ }
28729
28846
  }
28730
28847
  const template = JSON.parse(await readFile2(resolve3(dependencies.deploymentRoot, "wrangler-template.json"), "utf8"));
28731
28848
  template.name = serviceName;
@@ -28741,8 +28858,24 @@ var deployArtifactShare = async (input, dependencies) => {
28741
28858
  template.routes = [{ pattern: input.hostname, custom_domain: true }];
28742
28859
  template.workers_dev = false;
28743
28860
  template.preview_urls = false;
28744
- template.vars.ACCESS_TEAM_DOMAIN = `https://${organization.auth_domain}`;
28745
- template.vars.ACCESS_AUD = application.aud;
28861
+ if (input.publicAuth === void 0) {
28862
+ if (application === void 0) throw new Error("Cloudflare Access application is unavailable");
28863
+ delete template.secrets;
28864
+ template.vars.HUMAN_AUTH_MODE = "cloudflare-access";
28865
+ template.vars.ACCESS_TEAM_DOMAIN = `https://${organization.auth_domain}`;
28866
+ template.vars.ACCESS_AUD = application.aud;
28867
+ } else {
28868
+ template.secrets = {
28869
+ required: ["GOOGLE_OAUTH_CLIENT_SECRET", "GITHUB_OAUTH_CLIENT_SECRET"]
28870
+ };
28871
+ delete template.vars.ACCESS_TEAM_DOMAIN;
28872
+ delete template.vars.ACCESS_AUD;
28873
+ template.vars.HUMAN_AUTH_MODE = "artifactpass";
28874
+ template.vars.GOOGLE_OAUTH_CLIENT_ID = input.publicAuth.googleClientId;
28875
+ template.vars.GITHUB_OAUTH_CLIENT_ID = input.publicAuth.githubClientId;
28876
+ template.vars.ALLOWED_EXPIRY_SECONDS = "900,1800,3600";
28877
+ template.vars.MAX_EXPIRY_SECONDS = "3600";
28878
+ }
28746
28879
  template.vars.PDF_PROVENANCE_KEY_ID = input.pdfKeyId;
28747
28880
  template.vars.PDF_PROVENANCE_PUBLIC_KEYS = JSON.stringify({ [input.pdfKeyId]: input.pdfPublicKey });
28748
28881
  template.vars.PDF_PROVENANCE_RENDERERS = "artifact-share-qualified-pdf@1";
@@ -28770,6 +28903,15 @@ var deployArtifactShare = async (input, dependencies) => {
28770
28903
  resolve3(dependencies.deploymentRoot, "storage-lifecycle.json"),
28771
28904
  "--force"
28772
28905
  ], { env: commandEnvironment });
28906
+ if (input.publicAuth !== void 0) {
28907
+ await runner("wrangler", ["secret", "bulk", "--config", configurationPath], {
28908
+ env: commandEnvironment,
28909
+ input: JSON.stringify({
28910
+ GOOGLE_OAUTH_CLIENT_SECRET: input.publicAuth.googleClientSecret,
28911
+ GITHUB_OAUTH_CLIENT_SECRET: input.publicAuth.githubClientSecret
28912
+ })
28913
+ });
28914
+ }
28773
28915
  await runner("wrangler", ["deploy", "--config", configurationPath, "--strict"], { env: commandEnvironment });
28774
28916
  changed.push("Worker deployment");
28775
28917
  } finally {
@@ -28778,16 +28920,113 @@ var deployArtifactShare = async (input, dependencies) => {
28778
28920
  const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
28779
28921
  const sleep2 = dependencies.sleep ?? (async (milliseconds) => await new Promise((resolveSleep) => setTimeout(resolveSleep, milliseconds)));
28780
28922
  const readinessTimeoutMilliseconds = dependencies.readinessTimeoutMilliseconds ?? 1e4;
28781
- const response = await fetchAfterDeploymentPropagation(
28923
+ await fetchAfterDeploymentPropagation(
28782
28924
  fetchImplementation,
28783
28925
  `${baseUrl}/health`,
28784
28926
  { redirect: "error" },
28785
28927
  sleep2,
28786
- readinessTimeoutMilliseconds
28928
+ readinessTimeoutMilliseconds,
28929
+ async (candidate) => {
28930
+ if (!candidate.ok) return false;
28931
+ const candidateHealth = await candidate.json().catch(() => null);
28932
+ if (candidateHealth?.service !== "lordebuilds.artifacts.share" || candidateHealth.status !== "ok") {
28933
+ return false;
28934
+ }
28935
+ return input.publicAuth === void 0 || candidateHealth.human_auth_mode === "artifactpass" && candidateHealth.authentication_configured === true;
28936
+ }
28787
28937
  );
28788
- const health = await response.clone().json().catch(() => null);
28789
- if (!response.ok || health?.service !== "lordebuilds.artifacts.share" || health.status !== "ok") {
28790
- throw new Error(`Deployment health check failed (${response.status})`);
28938
+ if (input.publicAuth !== void 0) {
28939
+ const signIn = await fetchAfterDeploymentPropagation(
28940
+ fetchImplementation,
28941
+ `${baseUrl}/auth/sign-in?return_to=%2Fupload`,
28942
+ { redirect: "manual" },
28943
+ sleep2,
28944
+ readinessTimeoutMilliseconds
28945
+ );
28946
+ if (!signIn.ok || !(await signIn.text()).includes("Continue with Google")) {
28947
+ throw new Error(`ArtifactPass sign-in check failed (${signIn.status})`);
28948
+ }
28949
+ for (const provider of ["google", "github"]) {
28950
+ const login = await fetchAfterDeploymentPropagation(
28951
+ fetchImplementation,
28952
+ `${baseUrl}/auth/login/${provider}?return_to=%2Fupload`,
28953
+ { redirect: "manual" },
28954
+ sleep2,
28955
+ readinessTimeoutMilliseconds
28956
+ );
28957
+ const location = login.headers.get("location");
28958
+ const expectedOrigin = provider === "google" ? "https://accounts.google.com" : "https://github.com";
28959
+ if (login.status !== 302 || location === null || new URL(location).origin !== expectedOrigin) {
28960
+ throw new Error(`ArtifactPass ${provider} authentication check failed (${login.status})`);
28961
+ }
28962
+ }
28963
+ let removedLegacyAccess = false;
28964
+ if (application !== void 0) {
28965
+ await dependencies.client.request(
28966
+ `/accounts/${input.accountId}/access/apps/${application.id}`,
28967
+ { method: "DELETE" }
28968
+ );
28969
+ removedLegacyAccess = true;
28970
+ changed.push("Access application removal");
28971
+ }
28972
+ try {
28973
+ await fetchAfterDeploymentPropagation(
28974
+ fetchImplementation,
28975
+ `${baseUrl}/upload`,
28976
+ { redirect: "manual" },
28977
+ sleep2,
28978
+ readinessTimeoutMilliseconds,
28979
+ (candidate) => isArtifactPassUploadRedirect(candidate, baseUrl)
28980
+ );
28981
+ } catch (error51) {
28982
+ if (removedLegacyAccess) {
28983
+ try {
28984
+ const restoredApplication = await restoreLegacyAccess(
28985
+ input,
28986
+ serviceName,
28987
+ legacyAccessPolicies,
28988
+ dependencies
28989
+ );
28990
+ if (typeof restoredApplication.aud !== "string" || restoredApplication.aud.length === 0 || !/^[a-z0-9-]+\.cloudflareaccess\.com$/u.test(organization.auth_domain)) {
28991
+ throw new Error("Restored Cloudflare Access configuration is incomplete");
28992
+ }
28993
+ const rollbackTemplate = structuredClone(template);
28994
+ delete rollbackTemplate.secrets;
28995
+ rollbackTemplate.vars.HUMAN_AUTH_MODE = "cloudflare-access";
28996
+ rollbackTemplate.vars.ACCESS_TEAM_DOMAIN = `https://${organization.auth_domain}`;
28997
+ rollbackTemplate.vars.ACCESS_AUD = restoredApplication.aud;
28998
+ delete rollbackTemplate.vars.GOOGLE_OAUTH_CLIENT_ID;
28999
+ delete rollbackTemplate.vars.GITHUB_OAUTH_CLIENT_ID;
29000
+ const rollbackRoot = await mkdtemp(resolve3(tmpdir(), "artifact-share-rollback-"));
29001
+ try {
29002
+ const rollbackConfigurationPath = resolve3(rollbackRoot, "wrangler.json");
29003
+ await writeFile2(rollbackConfigurationPath, JSON.stringify(rollbackTemplate), { mode: 384 });
29004
+ await runner("wrangler", ["deploy", "--config", rollbackConfigurationPath, "--strict"], {
29005
+ env: { CLOUDFLARE_ACCOUNT_ID: input.accountId }
29006
+ });
29007
+ } finally {
29008
+ await rm(rollbackRoot, { recursive: true });
29009
+ }
29010
+ await fetchAfterDeploymentPropagation(
29011
+ fetchImplementation,
29012
+ `${baseUrl}/upload`,
29013
+ { redirect: "manual" },
29014
+ sleep2,
29015
+ readinessTimeoutMilliseconds,
29016
+ (candidate) => [302, 303, 307, 401, 403].includes(candidate.status)
29017
+ );
29018
+ } catch (restoreError) {
29019
+ throw new AggregateError(
29020
+ [error51, restoreError],
29021
+ "ArtifactPass upload verification failed and the legacy Access gate could not be restored"
29022
+ );
29023
+ }
29024
+ }
29025
+ throw new Error("ArtifactPass did not protect the upload route after deployment propagation", {
29026
+ cause: error51
29027
+ });
29028
+ }
29029
+ return { baseUrl, teamCommand, plan, changed };
28791
29030
  }
28792
29031
  const protectedUpload = await fetchAfterDeploymentPropagation(
28793
29032
  fetchImplementation,
@@ -28956,7 +29195,7 @@ var claudePluginEntries = (value2) => {
28956
29195
  }
28957
29196
  return value2;
28958
29197
  };
28959
- var installPluginForHosts = async (hosts, marketplaceSource, runner = runProcess) => {
29198
+ var installPluginForHosts = async (hosts, marketplaceSource, binding, runner = runProcess) => {
28960
29199
  const rollbackActions = [];
28961
29200
  const rollback = async () => {
28962
29201
  const errors = [];
@@ -28993,6 +29232,21 @@ var installPluginForHosts = async (hosts, marketplaceSource, runner = runProcess
28993
29232
  if (hadPlugin) await runner("codex", ["plugin", "add", artifactpassPluginId, "--json"]);
28994
29233
  });
28995
29234
  await runner("codex", ["plugin", "add", artifactpassPluginId, "--json"]);
29235
+ await runner("codex", [
29236
+ "mcp",
29237
+ "add",
29238
+ "artifactpass",
29239
+ "--env",
29240
+ `ARTIFACTPASS_CONFIG_PATH=${binding.configPath}`,
29241
+ "--env",
29242
+ `ARTIFACTPASS_PROFILE=${binding.profileName}`,
29243
+ "--",
29244
+ process.execPath,
29245
+ binding.bridgePath
29246
+ ]);
29247
+ rollbackActions.push(async () => {
29248
+ await runner("codex", ["mcp", "remove", "artifactpass"]);
29249
+ });
28996
29250
  }
28997
29251
  if (hosts.includes("claude")) {
28998
29252
  const marketplaces = namedEntries(parseJson(
@@ -29039,6 +29293,23 @@ var installPluginForHosts = async (hosts, marketplaceSource, runner = runProcess
29039
29293
  "--scope",
29040
29294
  "user"
29041
29295
  ]);
29296
+ await runner("claude", [
29297
+ "mcp",
29298
+ "add",
29299
+ "--scope",
29300
+ "user",
29301
+ "artifactpass",
29302
+ "-e",
29303
+ `ARTIFACTPASS_CONFIG_PATH=${binding.configPath}`,
29304
+ "-e",
29305
+ `ARTIFACTPASS_PROFILE=${binding.profileName}`,
29306
+ "--",
29307
+ process.execPath,
29308
+ binding.bridgePath
29309
+ ]);
29310
+ rollbackActions.push(async () => {
29311
+ await runner("claude", ["mcp", "remove", "artifactpass", "--scope", "user"]);
29312
+ });
29042
29313
  }
29043
29314
  return { hosts, rollback };
29044
29315
  } catch (error51) {
@@ -29482,7 +29753,16 @@ var connectHost = async (input, dependencies) => {
29482
29753
  sourceRoot: resolve5(input.marketplaceSource, "plugins/artifactpass")
29483
29754
  }) : void 0;
29484
29755
  const installAndVerify = async () => {
29485
- const hostInstallation = installKnownHostAdapters && hosts.length > 0 ? await installPluginForHosts(hosts, input.marketplaceSource, runner) : void 0;
29756
+ const hostInstallation = installKnownHostAdapters && hosts.length > 0 ? await installPluginForHosts(hosts, input.marketplaceSource, {
29757
+ configPath,
29758
+ profileName,
29759
+ bridgePath: resolve5(
29760
+ input.hostBridgePath ?? resolve5(
29761
+ input.marketplaceSource,
29762
+ "plugins/artifactpass/dist/cli.mjs"
29763
+ )
29764
+ )
29765
+ }, runner) : void 0;
29486
29766
  try {
29487
29767
  await dependencies.verifyConnection?.({
29488
29768
  configPath,
@@ -29532,15 +29812,26 @@ var connectHost = async (input, dependencies) => {
29532
29812
  ...migration === void 0 ? {} : { migration }
29533
29813
  };
29534
29814
  }
29535
- const previousToken = await store.get();
29536
- if (previousToken !== null && previousProfile === void 0) {
29815
+ const previousStoredCredential = await store.get();
29816
+ if (previousStoredCredential !== null && previousProfile === void 0) {
29537
29817
  throw new Error(`The existing ArtifactPass ${profileName} credential has no matching profile; disconnect it first`);
29538
29818
  }
29539
- if (previousToken !== null && previousProfile !== void 0 && previousProfile.open_development !== true) {
29819
+ const previousToken = previousStoredCredential === null || previousProfile === void 0 ? null : resolveAgentCredential(
29820
+ previousStoredCredential,
29821
+ previousProfile.base_url,
29822
+ previousProfile.credential_binding === "origin"
29823
+ );
29824
+ if (previousToken !== null && previousProfile !== void 0 && previousProfile.open_development !== true && new URL(previousProfile.base_url).origin === origin.origin) {
29540
29825
  const inspection = await inspectToken(origin, previousToken, fetchImplementation);
29541
29826
  if (inspection !== null) {
29542
29827
  if (previousSettings === null) throw new Error("ArtifactPass profile state disappeared");
29828
+ const boundCredential = bindAgentCredential(origin, previousToken);
29829
+ let migratedCredential = false;
29543
29830
  try {
29831
+ if (boundCredential !== previousStoredCredential) {
29832
+ await store.set(boundCredential);
29833
+ migratedCredential = true;
29834
+ }
29544
29835
  await (dependencies.writeSettings ?? writeLocalBridgeSettings)(configPath, upsertLocalBridgeProfile(
29545
29836
  previousSettings,
29546
29837
  profileName,
@@ -29550,11 +29841,13 @@ var connectHost = async (input, dependencies) => {
29550
29841
  ...previousProfile.publication_state === "legacy" ? { publication_state: "legacy" } : {},
29551
29842
  ...previousProfile.publication_state_path === void 0 ? {} : { publication_state_path: previousProfile.publication_state_path },
29552
29843
  credential_namespace: "artifactpass",
29844
+ credential_binding: "origin",
29553
29845
  ...healthBody.pdf_provenance_key_id === void 0 ? previousProfile.pdf_key_id === void 0 ? {} : { pdf_key_id: previousProfile.pdf_key_id } : { pdf_key_id: healthBody.pdf_provenance_key_id }
29554
29846
  }
29555
29847
  ));
29556
29848
  await installAndVerify();
29557
29849
  } catch (error51) {
29850
+ if (migratedCredential) await store.set(previousStoredCredential);
29558
29851
  await (dependencies.writeSettings ?? writeLocalBridgeSettings)(configPath, previousSettings).catch((rollbackError) => {
29559
29852
  throw new AggregateError([error51, rollbackError], "ArtifactPass connection verification failed and config rollback was incomplete");
29560
29853
  });
@@ -29587,11 +29880,12 @@ var connectHost = async (input, dependencies) => {
29587
29880
  ...previousProfile?.publication_state === "legacy" ? { publication_state: "legacy" } : {},
29588
29881
  ...previousProfile?.publication_state_path === void 0 ? {} : { publication_state_path: previousProfile.publication_state_path },
29589
29882
  credential_namespace: "artifactpass",
29883
+ credential_binding: "origin",
29590
29884
  ...healthBody.pdf_provenance_key_id === void 0 ? {} : { pdf_key_id: healthBody.pdf_provenance_key_id }
29591
29885
  }
29592
29886
  ));
29593
29887
  wroteConfig = true;
29594
- await store.set(token.accessToken);
29888
+ await store.set(bindAgentCredential(origin, token.accessToken));
29595
29889
  rollbackHostInstallation = await installAndVerify();
29596
29890
  if (previousToken !== null && previousProfile !== void 0 && previousProfile.open_development !== true) {
29597
29891
  await revokeToken(
@@ -29608,10 +29902,10 @@ var connectHost = async (input, dependencies) => {
29608
29902
  await revokeToken(origin, token.accessToken, fetchImplementation).catch((cleanupError) => {
29609
29903
  cleanupErrors.push(cleanupError);
29610
29904
  });
29611
- if (previousToken === null) {
29905
+ if (previousStoredCredential === null) {
29612
29906
  await store.delete().catch((cleanupError) => cleanupErrors.push(cleanupError));
29613
29907
  } else {
29614
- await store.set(previousToken).catch((cleanupError) => cleanupErrors.push(cleanupError));
29908
+ await store.set(previousStoredCredential).catch((cleanupError) => cleanupErrors.push(cleanupError));
29615
29909
  }
29616
29910
  if (wroteConfig) {
29617
29911
  const restoreConfig = previousSettings === null ? rm4(configPath, { force: true }) : (dependencies.writeSettings ?? writeLocalBridgeSettings)(configPath, previousSettings);
@@ -29627,7 +29921,7 @@ var connectHost = async (input, dependencies) => {
29627
29921
  profileName,
29628
29922
  expiresIn: token.expiresIn,
29629
29923
  configPath,
29630
- credentialAction: previousToken === null ? "created" : "rotated",
29924
+ credentialAction: previousStoredCredential === null ? "created" : "rotated",
29631
29925
  ...portableIntegration === void 0 ? {} : { portableIntegration },
29632
29926
  ...migration === void 0 ? {} : { migration }
29633
29927
  };
@@ -29663,9 +29957,10 @@ var disconnectHost = async (baseUrl, options = {}) => {
29663
29957
  }),
29664
29958
  migrationCommitted: true
29665
29959
  });
29666
- const token = await store.get();
29667
- if (token === null) return;
29960
+ const storedCredential = await store.get();
29961
+ if (storedCredential === null) return;
29668
29962
  const origin = assertSafeDeploymentOrigin(new URL(baseUrl));
29963
+ const token = resolveAgentCredential(storedCredential, origin);
29669
29964
  const response = await (options.fetch ?? globalThis.fetch)(new URL("/api/connection", origin), {
29670
29965
  method: "DELETE",
29671
29966
  headers: { Authorization: `Bearer ${token}` },
@@ -29677,6 +29972,45 @@ var disconnectHost = async (baseUrl, options = {}) => {
29677
29972
  await store.delete();
29678
29973
  };
29679
29974
 
29975
+ // src/cli-arguments.ts
29976
+ var CONNECT_VALUE_OPTIONS = /* @__PURE__ */ new Set([
29977
+ "--profile",
29978
+ "--workspace-root",
29979
+ "--host",
29980
+ "--marketplace"
29981
+ ]);
29982
+ var CONNECT_BOOLEAN_OPTIONS = /* @__PURE__ */ new Set([
29983
+ "--no-host-install",
29984
+ "--open-development"
29985
+ ]);
29986
+ var parseConnectArguments = (args) => {
29987
+ const positionals = [];
29988
+ for (let index = 0; index < args.length; index += 1) {
29989
+ const argument = args[index] ?? "";
29990
+ if (CONNECT_VALUE_OPTIONS.has(argument)) {
29991
+ const optionValue = args[index + 1];
29992
+ if (optionValue === void 0 || optionValue.startsWith("--")) {
29993
+ throw new Error(`${argument} requires a value`);
29994
+ }
29995
+ index += 1;
29996
+ continue;
29997
+ }
29998
+ if (CONNECT_BOOLEAN_OPTIONS.has(argument)) continue;
29999
+ if (argument.startsWith("--")) throw new Error(`Unknown connect option: ${argument}`);
30000
+ positionals.push(argument);
30001
+ }
30002
+ if (positionals.length > 1) throw new Error("connect accepts at most one deployment URL");
30003
+ return positionals[0] === void 0 ? {} : { deploymentUrl: positionals[0] };
30004
+ };
30005
+ var resolveConnectDeploymentUrl = (parsed, settings, requestedProfile) => {
30006
+ if (parsed.deploymentUrl !== void 0) return parsed.deploymentUrl;
30007
+ if (requestedProfile !== void 0 && settings?.profiles[requestedProfile] === void 0) {
30008
+ throw new Error(`Unknown ArtifactPass profile: ${requestedProfile}; provide its deployment URL to create it`);
30009
+ }
30010
+ const profileName = requestedProfile ?? settings?.active_profile;
30011
+ return (profileName === void 0 ? void 0 : settings?.profiles[profileName]?.base_url) ?? "https://artifactpass.com";
30012
+ };
30013
+
29680
30014
  // src/doctor.ts
29681
30015
  import { access, constants } from "node:fs/promises";
29682
30016
  var commandExists = async (runner, command) => {
@@ -29726,8 +30060,12 @@ import { dirname as dirname6, join as join2, parse as parse3, resolve as resolve
29726
30060
  // package.json
29727
30061
  var package_default = {
29728
30062
  name: "artifactpass",
29729
- version: "0.1.0-rc.2",
30063
+ version: "0.1.0-rc.4",
29730
30064
  description: "Install and connect ArtifactPass for compatible AI agent systems.",
30065
+ repository: {
30066
+ type: "git",
30067
+ url: "git+https://github.com/lordelogos/lordebuilds.artifact.pass.git"
30068
+ },
29731
30069
  type: "module",
29732
30070
  bin: {
29733
30071
  artifactpass: "dist/cli.mjs"
@@ -29890,9 +30228,11 @@ var renderInstallReceipt = (receipt) => {
29890
30228
  return `ArtifactPass installation failed during ${receipt.failed_stage ?? "setup"}. Previous working state was restored.`;
29891
30229
  }
29892
30230
  const registration = receipt.portable_bundle.host_registration === "manual-required" ? `Manual host registration required. MCP: ${receipt.portable_bundle.mcp_config ?? "unavailable"}; skills: ${receipt.portable_bundle.skills_directory ?? "unavailable"}.` : `Installed for ${receipt.adapters.join(" and ")}.`;
29893
- return `ArtifactPass is connected to ${receipt.origin} for ${receipt.profile}. ${registration} Start a new agent session before using it.`;
30231
+ const connection = receipt.credential === "none" ? `ArtifactPass is installed for ${receipt.profile} but not connected. Run \`pnpm dlx artifactpass connect ${receipt.origin} --profile ${receipt.profile}\` when you want to publish.` : `ArtifactPass is connected to ${receipt.origin} for ${receipt.profile}.`;
30232
+ return `${connection} ${registration} Start a new agent session before using it.`;
29894
30233
  };
29895
30234
  var runArtifactpassInstall = async (input, dependencies) => {
30235
+ const connectAfterInstall = input.connectAfterInstall === true;
29896
30236
  const now = dependencies.now ?? Date.now;
29897
30237
  const operationId = (dependencies.operationId ?? randomUUID3)();
29898
30238
  const origin = new URL(input.baseUrl ?? "https://artifactpass.com").origin;
@@ -29913,6 +30253,7 @@ var runArtifactpassInstall = async (input, dependencies) => {
29913
30253
  let smoke;
29914
30254
  let skills = [];
29915
30255
  let connectResult;
30256
+ let hostInstallation;
29916
30257
  let configSnapshot;
29917
30258
  const outcomes = [];
29918
30259
  try {
@@ -29932,7 +30273,7 @@ var runArtifactpassInstall = async (input, dependencies) => {
29932
30273
  if (isMissing(error51)) return { existed: false };
29933
30274
  throw error51;
29934
30275
  });
29935
- if (dependencies.skipCredentialStorePreflight !== true && input.openDevelopment !== true) {
30276
+ if (connectAfterInstall && dependencies.skipCredentialStorePreflight !== true && input.openDevelopment !== true) {
29936
30277
  await verifyCredentialStore(
29937
30278
  dependencies.platform ?? process.platform,
29938
30279
  dependencies.runner ?? runProcess
@@ -29952,24 +30293,74 @@ var runArtifactpassInstall = async (input, dependencies) => {
29952
30293
  stage = "connection";
29953
30294
  journal = { ...journal, stage };
29954
30295
  await writeJson(journalPath, journal);
29955
- connectResult = await (dependencies.connect ?? connectHost)({
29956
- baseUrl: origin,
29957
- profileName,
29958
- workspaceRoots: [workspaceRoot],
29959
- marketplaceSource: input.marketplaceSource,
29960
- openDevelopment: input.openDevelopment === true,
29961
- installKnownHostAdapters: input.installKnownHostAdapters !== false,
29962
- ...input.configPath === void 0 ? {} : { configPath }
29963
- }, {
29964
- ...dependencies.connectDependencies,
29965
- verifyConnection: async (context) => {
29966
- smoke = await (dependencies.smoke ?? smokeArtifactpassMcp)({
29967
- mcpConfigPath: portable?.mcpConfig ?? "",
29968
- localConfigPath: context.configPath
29969
- });
29970
- skills = await verifySkills(portable);
29971
- }
29972
- });
30296
+ if (connectAfterInstall) {
30297
+ connectResult = await (dependencies.connect ?? connectHost)({
30298
+ baseUrl: origin,
30299
+ profileName,
30300
+ workspaceRoots: [workspaceRoot],
30301
+ marketplaceSource: input.marketplaceSource,
30302
+ hostBridgePath: resolve6(portable.rootDirectory, "plugin/dist/cli.mjs"),
30303
+ openDevelopment: input.openDevelopment === true,
30304
+ installKnownHostAdapters: input.installKnownHostAdapters !== false,
30305
+ ...input.configPath === void 0 ? {} : { configPath }
30306
+ }, {
30307
+ ...dependencies.connectDependencies,
30308
+ verifyConnection: async (context) => {
30309
+ [smoke, skills] = await Promise.all([
30310
+ (dependencies.smoke ?? smokeArtifactpassMcp)({
30311
+ mcpConfigPath: portable?.mcpConfig ?? "",
30312
+ localConfigPath: context.configPath
30313
+ }),
30314
+ verifySkills(portable)
30315
+ ]);
30316
+ }
30317
+ });
30318
+ } else {
30319
+ const previousSettings = configSnapshot?.existed === true && configSnapshot.bytes !== void 0 ? await readLocalBridgeSettings(configPath) : null;
30320
+ const previousProfile = previousSettings?.profiles[profileName];
30321
+ if (previousProfile !== void 0 && new URL(previousProfile.base_url).origin !== new URL(origin).origin) {
30322
+ throw new Error(
30323
+ `ArtifactPass ${profileName} already targets ${new URL(previousProfile.base_url).origin}; use a new profile or disconnect it before changing deployments`
30324
+ );
30325
+ }
30326
+ const installedSettings = upsertLocalBridgeProfile(
30327
+ previousSettings,
30328
+ profileName,
30329
+ {
30330
+ base_url: origin,
30331
+ workspace_roots: [workspaceRoot],
30332
+ ...input.openDevelopment === true ? { open_development: true } : {},
30333
+ ...previousProfile?.pdf_key_id === void 0 ? {} : { pdf_key_id: previousProfile.pdf_key_id },
30334
+ ...previousProfile?.publication_state === "legacy" ? { publication_state: "legacy" } : {},
30335
+ ...previousProfile?.publication_state_path === void 0 ? {} : { publication_state_path: previousProfile.publication_state_path },
30336
+ credential_namespace: "artifactpass",
30337
+ ...previousProfile?.credential_binding === "origin" ? { credential_binding: "origin" } : {}
30338
+ }
30339
+ );
30340
+ await writeLocalBridgeSettings(
30341
+ configPath,
30342
+ previousSettings !== null && input.profileName === void 0 && input.openDevelopment !== true ? setActiveLocalBridgeProfile(installedSettings, previousSettings.active_profile) : installedSettings
30343
+ );
30344
+ const runner = dependencies.runner ?? runProcess;
30345
+ const hosts = input.installKnownHostAdapters === false ? [] : await detectHosts(runner);
30346
+ hostInstallation = hosts.length === 0 ? void 0 : await installPluginForHosts(hosts, input.marketplaceSource, {
30347
+ configPath,
30348
+ profileName,
30349
+ bridgePath: resolve6(portable.rootDirectory, "plugin/dist/cli.mjs")
30350
+ }, runner);
30351
+ [smoke, skills] = await Promise.all([
30352
+ (dependencies.smoke ?? smokeArtifactpassMcp)({
30353
+ mcpConfigPath: portable.mcpConfig,
30354
+ localConfigPath: configPath
30355
+ }),
30356
+ verifySkills(portable)
30357
+ ]);
30358
+ connectResult = {
30359
+ hosts,
30360
+ profileName,
30361
+ configPath
30362
+ };
30363
+ }
29973
30364
  outcomes.push(`credential:${connectResult.credentialAction ?? "none"}`);
29974
30365
  outcomes.push("host-registration:installed-or-reported");
29975
30366
  await dependencies.afterStage?.("connection");
@@ -30027,6 +30418,11 @@ var runArtifactpassInstall = async (input, dependencies) => {
30027
30418
  return receipt;
30028
30419
  } catch (error51) {
30029
30420
  const rollbackFailures = [];
30421
+ if (hostInstallation !== void 0) {
30422
+ await hostInstallation.rollback().catch(() => rollbackFailures.push("host-registration"));
30423
+ } else if ((connectResult?.hosts.length ?? 0) > 0) {
30424
+ rollbackFailures.push("host-registration-unverified");
30425
+ }
30030
30426
  if (portable !== void 0 && (dependencies.portableWasCreated ?? portableIntegrationWasCreated)(portable)) {
30031
30427
  await rm5(portable.rootDirectory, { recursive: true, force: true }).catch(() => rollbackFailures.push("portable-bundle"));
30032
30428
  }
@@ -30034,7 +30430,6 @@ var runArtifactpassInstall = async (input, dependencies) => {
30034
30430
  const restore = configSnapshot.existed && configSnapshot.bytes !== void 0 ? writeFile5(configPath, configSnapshot.bytes, { mode: 384 }) : rm5(configPath, { force: true });
30035
30431
  await restore.catch(() => rollbackFailures.push("configuration"));
30036
30432
  }
30037
- if ((connectResult?.hosts.length ?? 0) > 0) rollbackFailures.push("host-registration-unverified");
30038
30433
  if (connectResult?.credentialAction === "created" || connectResult?.credentialAction === "rotated") {
30039
30434
  rollbackFailures.push("credential-unverified");
30040
30435
  }
@@ -30117,11 +30512,6 @@ var booleanFlag = (args, flag) => {
30117
30512
  return count === 1;
30118
30513
  };
30119
30514
  var positionalValues = (args) => args.filter((item, index) => index === 0 || !args[index - 1]?.startsWith("--")).filter((item) => !item.startsWith("--"));
30120
- var positional = (args, position) => {
30121
- const result = positionalValues(args)[position];
30122
- if (result === void 0) throw new Error("A deployment URL is required");
30123
- return result;
30124
- };
30125
30515
  var optionalPositional = (args, position) => positionalValues(args)[position];
30126
30516
  var print = (valueToPrint) => {
30127
30517
  process.stdout.write(`${typeof valueToPrint === "string" ? valueToPrint : JSON.stringify(valueToPrint, null, 2)}
@@ -30132,14 +30522,20 @@ var help = `ArtifactPass setup
30132
30522
  Commands:
30133
30523
  artifactpass [--json]
30134
30524
  install [--base-url <url>] [--profile <name>] [--workspace-root <path>] [--open-development] [--no-host-install] [--json]
30525
+ deploy-public --account-id <id> --zone-id <id> --hostname <host> [--service-name <name>] --workers-subdomain <name> --pdf-key-id <id> --pdf-public-key <base64> --google-client-id <id> --github-client-id <id> (--dry-run | --write-approval-manifest <path> | --approve-manifest <path>)
30135
30526
  deploy --account-id <id> --zone-id <id> --hostname <host> [--service-name <name>] --workers-subdomain <name> --pdf-key-id <id> --pdf-public-key <base64> (--allow-email <email> | --allow-domain <domain>) (--dry-run | --write-approval-manifest <path> | --approve-manifest <path>)
30136
- connect <base-url> [--profile <name>] [--workspace-root <path>] [--host codex|claude|both] [--no-host-install] [--marketplace <source>] [--open-development]
30527
+ connect [base-url] [--profile <name>] [--workspace-root <path>] [--host codex|claude|both] [--no-host-install] [--marketplace <source>] [--open-development]
30137
30528
  profile list
30138
30529
  profile use <name>
30139
30530
  disconnect [--profile <name>]
30140
30531
  doctor
30141
30532
 
30142
30533
  Cloudflare credentials come from CLOUDFLARE_API_TOKEN or Wrangler OAuth and are never persisted by ArtifactPass.`;
30534
+ var requiredEnvironment = (name) => {
30535
+ const result = process.env[name];
30536
+ if (result === void 0 || result.length === 0) throw new Error(`${name} is required`);
30537
+ return result;
30538
+ };
30143
30539
  var jsonOutputRequested = false;
30144
30540
  var main = async () => {
30145
30541
  const [command, ...args] = process.argv.slice(2);
@@ -30163,6 +30559,7 @@ var main = async () => {
30163
30559
  ...profileName === void 0 ? {} : { profileName },
30164
30560
  ...workspaceRoot === void 0 ? {} : { workspaceRoot },
30165
30561
  openDevelopment,
30562
+ connectAfterInstall: false,
30166
30563
  installKnownHostAdapters: !booleanFlag(installArgs, "--no-host-install")
30167
30564
  }, {
30168
30565
  connectDependencies: {
@@ -30189,6 +30586,39 @@ ${url2}
30189
30586
  if (!result.node || !result.wrangler || !result.deploymentAssets) process.exitCode = 1;
30190
30587
  return;
30191
30588
  }
30589
+ if (command === "deploy-public") {
30590
+ const dryRun = booleanFlag(args, "--dry-run");
30591
+ const writeApprovalManifest = optionalValue(args, "--write-approval-manifest");
30592
+ const approveManifest = optionalValue(args, "--approve-manifest");
30593
+ if ([dryRun, writeApprovalManifest !== void 0, approveManifest !== void 0].filter(Boolean).length !== 1) {
30594
+ throw new Error("Choose exactly one of --dry-run, --write-approval-manifest, or --approve-manifest");
30595
+ }
30596
+ const serviceName = optionalValue(args, "--service-name");
30597
+ const result = await runDeployCommand({
30598
+ accountId: value(args, "--account-id"),
30599
+ zoneId: value(args, "--zone-id"),
30600
+ hostname: value(args, "--hostname"),
30601
+ pdfKeyId: value(args, "--pdf-key-id"),
30602
+ pdfPublicKey: value(args, "--pdf-public-key"),
30603
+ workersSubdomain: value(args, "--workers-subdomain"),
30604
+ identities: [],
30605
+ dryRun,
30606
+ publicAuth: {
30607
+ googleClientId: value(args, "--google-client-id"),
30608
+ googleClientSecret: requiredEnvironment("ARTIFACTPASS_GOOGLE_OAUTH_CLIENT_SECRET"),
30609
+ githubClientId: value(args, "--github-client-id"),
30610
+ githubClientSecret: requiredEnvironment("ARTIFACTPASS_GITHUB_OAUTH_CLIENT_SECRET")
30611
+ },
30612
+ ...serviceName === void 0 ? {} : { serviceName },
30613
+ ...writeApprovalManifest === void 0 ? {} : { writeApprovalManifest: resolve7(writeApprovalManifest) },
30614
+ ...approveManifest === void 0 ? {} : { approveManifest: resolve7(approveManifest) }
30615
+ }, {
30616
+ deploymentRoot,
30617
+ ...process.env.CLOUDFLARE_API_TOKEN === void 0 ? {} : { token: process.env.CLOUDFLARE_API_TOKEN }
30618
+ });
30619
+ print(result);
30620
+ return;
30621
+ }
30192
30622
  if (command === "deploy") {
30193
30623
  const dryRun = booleanFlag(args, "--dry-run");
30194
30624
  const writeApprovalManifest = optionalValue(args, "--write-approval-manifest");
@@ -30221,6 +30651,12 @@ ${url2}
30221
30651
  return;
30222
30652
  }
30223
30653
  if (command === "connect") {
30654
+ const connectArguments = parseConnectArguments(args);
30655
+ await migrateDefaultLocalState();
30656
+ const savedSettings = await readLocalBridgeSettings(defaultLocalConfigPath()).catch((error51) => {
30657
+ if (error51.code === "ENOENT") return null;
30658
+ throw error51;
30659
+ });
30224
30660
  const installKnownHostAdapters = !booleanFlag(args, "--no-host-install");
30225
30661
  const requestedHost = optionalValue(args, "--host");
30226
30662
  const host = requestedHost ?? "both";
@@ -30230,17 +30666,18 @@ ${url2}
30230
30666
  }
30231
30667
  const hosts = host === "both" ? void 0 : [host];
30232
30668
  const marketplaceSource = optionalValue(args, "--marketplace") ?? defaultMarketplace;
30233
- const portableIntegration = installKnownHostAdapters ? void 0 : await installPortableIntegration({
30669
+ const portableIntegration = await installPortableIntegration({
30234
30670
  sourceRoot: resolve7(marketplaceSource, "plugins/artifactpass")
30235
30671
  });
30236
30672
  const profileName = optionalValue(args, "--profile");
30237
30673
  const result = await connectHost({
30238
30674
  ...profileName === void 0 ? {} : { profileName },
30239
- baseUrl: positional(args, 0),
30675
+ baseUrl: resolveConnectDeploymentUrl(connectArguments, savedSettings, profileName),
30240
30676
  workspaceRoots: values(args, "--workspace-root").length > 0 ? values(args, "--workspace-root") : [process.cwd()],
30241
30677
  ...hosts === void 0 ? {} : { hosts },
30242
30678
  installKnownHostAdapters,
30243
30679
  marketplaceSource,
30680
+ ...installKnownHostAdapters ? { hostBridgePath: resolve7(portableIntegration.rootDirectory, "plugin/dist/cli.mjs") } : {},
30244
30681
  openDevelopment: booleanFlag(args, "--open-development")
30245
30682
  }, {
30246
30683
  deviceFlowDependencies: {