@aiworkbench/vibe-publish 0.0.2 → 0.0.5

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/index.js ADDED
@@ -0,0 +1,392 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { parseArgs } from "node:util";
5
+
6
+ // src/config.ts
7
+ import { readFileSync } from "node:fs";
8
+ import { resolve, parse as parsePath } from "node:path";
9
+ function loadDotenv(startDir = process.cwd()) {
10
+ let dir = resolve(startDir);
11
+ const { root } = parsePath(dir);
12
+ while (dir !== root) {
13
+ try {
14
+ const raw = readFileSync(resolve(dir, ".env"), "utf-8");
15
+ for (const line of raw.split(`
16
+ `)) {
17
+ const trimmed = line.trim();
18
+ if (!trimmed || trimmed.startsWith("#"))
19
+ continue;
20
+ const eqIdx = trimmed.indexOf("=");
21
+ if (eqIdx === -1)
22
+ continue;
23
+ const key = trimmed.slice(0, eqIdx).trim();
24
+ let value = trimmed.slice(eqIdx + 1).trim();
25
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
26
+ value = value.slice(1, -1);
27
+ }
28
+ if (process.env[key] === undefined) {
29
+ process.env[key] = value;
30
+ }
31
+ }
32
+ return;
33
+ } catch {
34
+ dir = resolve(dir, "..");
35
+ }
36
+ }
37
+ }
38
+ function envScoped(key, environment) {
39
+ const scoped = process.env[`${key}_${environment.toUpperCase()}`];
40
+ if (scoped)
41
+ return scoped;
42
+ return process.env[key];
43
+ }
44
+ function loadConfig(overrides = {}) {
45
+ loadDotenv();
46
+ const env = overrides.environment ?? process.env.VIBE_ENVIRONMENT ?? "dev";
47
+ return {
48
+ environment: env,
49
+ directPush: overrides.directPush ?? process.env.VIBE_DIRECT_PUSH === "true",
50
+ storageProvider: overrides.storageProvider ?? process.env.VIBE_STORAGE_PROVIDER ?? "azure-blob",
51
+ azureAccountName: overrides.azureAccountName ?? envScoped("VIBE_STORAGE_ACCOUNT_NAME", env),
52
+ azureAccountKey: overrides.azureAccountKey ?? envScoped("VIBE_STORAGE_ACCOUNT_KEY", env),
53
+ azureContainerName: overrides.azureContainerName ?? envScoped("VIBE_STORAGE_CONTAINER_NAME", env),
54
+ gitPlatform: overrides.gitPlatform ?? process.env.VIBE_GIT_PLATFORM ?? "github",
55
+ registryRepo: overrides.registryRepo ?? envScoped("VIBE_REGISTRY_REPO", env),
56
+ registryToken: overrides.registryToken ?? envScoped("VIBE_REGISTRY_TOKEN", env),
57
+ githubApiUrl: overrides.githubApiUrl ?? (process.env.VIBE_GITHUB_API_URL || undefined) ?? (process.env.GITHUB_API_URL || undefined) ?? "https://api.github.com",
58
+ githubServerUrl: overrides.githubServerUrl ?? (process.env.VIBE_GITHUB_SERVER_URL || undefined) ?? (process.env.GITHUB_SERVER_URL || undefined) ?? "https://github.com",
59
+ publishedBy: overrides.publishedBy ?? process.env.GITHUB_ACTOR ?? process.env.USER ?? "unknown"
60
+ };
61
+ }
62
+
63
+ // src/publish.ts
64
+ import { readFileSync as readFileSync3 } from "node:fs";
65
+ import { resolve as resolve2 } from "node:path";
66
+
67
+ // src/integrity.ts
68
+ import { createHash } from "node:crypto";
69
+ function computeIntegrity(content) {
70
+ const hash = createHash("sha384").update(content).digest("base64");
71
+ return `sha384-${hash}`;
72
+ }
73
+
74
+ // src/storage/azure-blob.ts
75
+ import {
76
+ BlobServiceClient,
77
+ StorageSharedKeyCredential
78
+ } from "@azure/storage-blob";
79
+
80
+ class AzureBlobProvider {
81
+ client;
82
+ containerName;
83
+ accountName;
84
+ constructor(config) {
85
+ this.accountName = config.accountName;
86
+ this.containerName = config.containerName;
87
+ const credential = new StorageSharedKeyCredential(config.accountName, config.accountKey);
88
+ this.client = new BlobServiceClient(`https://${config.accountName}.blob.core.windows.net`, credential);
89
+ }
90
+ getUrl(appId, version, filename) {
91
+ return `https://${this.accountName}.blob.core.windows.net/${this.containerName}/${appId}/${version}/${filename}`;
92
+ }
93
+ async exists(appId, version, filename) {
94
+ const container = this.client.getContainerClient(this.containerName);
95
+ const blob = container.getBlockBlobClient(`${appId}/${version}/${filename}`);
96
+ return blob.exists();
97
+ }
98
+ async upload(appId, version, filename, content, contentType) {
99
+ const container = this.client.getContainerClient(this.containerName);
100
+ const blob = container.getBlockBlobClient(`${appId}/${version}/${filename}`);
101
+ await blob.upload(content, content.length, {
102
+ blobHTTPHeaders: {
103
+ blobContentType: contentType,
104
+ blobCacheControl: "public, max-age=31536000, immutable"
105
+ }
106
+ });
107
+ return {
108
+ url: this.getUrl(appId, version, filename),
109
+ sizeBytes: content.length
110
+ };
111
+ }
112
+ }
113
+
114
+ // src/storage/index.ts
115
+ function resolveStorageProvider(config) {
116
+ switch (config.storageProvider) {
117
+ case "azure-blob":
118
+ if (!config.azureAccountName || !config.azureAccountKey || !config.azureContainerName) {
119
+ throw new Error("Azure Blob requires VIBE_STORAGE_ACCOUNT_NAME, VIBE_STORAGE_ACCOUNT_KEY, and VIBE_STORAGE_CONTAINER_NAME");
120
+ }
121
+ return new AzureBlobProvider({
122
+ accountName: config.azureAccountName,
123
+ accountKey: config.azureAccountKey,
124
+ containerName: config.azureContainerName
125
+ });
126
+ case "s3":
127
+ throw new Error('S3 provider is not yet implemented. Set VIBE_STORAGE_PROVIDER="azure-blob" or implement S3Provider.');
128
+ case "gcs":
129
+ throw new Error('GCS provider is not yet implemented. Set VIBE_STORAGE_PROVIDER="azure-blob" or implement GCSProvider.');
130
+ default:
131
+ throw new Error(`Unknown storage provider: "${config.storageProvider}". Supported: azure-blob, s3, gcs`);
132
+ }
133
+ }
134
+
135
+ // src/git-platform/github.ts
136
+ import { execFileSync } from "node:child_process";
137
+
138
+ class GitHubAdapter {
139
+ repo;
140
+ token;
141
+ apiUrl;
142
+ serverUrl;
143
+ hostname;
144
+ constructor(config) {
145
+ this.repo = config.registryRepo;
146
+ this.token = config.token;
147
+ this.apiUrl = config.apiUrl;
148
+ this.serverUrl = config.serverUrl;
149
+ this.hostname = new URL(config.serverUrl).hostname;
150
+ }
151
+ cloneUrl() {
152
+ return `https://x-access-token:${this.token}@${this.hostname}/${this.repo}.git`;
153
+ }
154
+ redact(text) {
155
+ return this.token ? text.replaceAll(this.token, "***") : text;
156
+ }
157
+ git(args, cwd) {
158
+ try {
159
+ return execFileSync("git", args, {
160
+ cwd,
161
+ encoding: "utf-8",
162
+ stdio: ["pipe", "pipe", "pipe"]
163
+ }).trim();
164
+ } catch (err) {
165
+ const msg = err instanceof Error ? err.message : String(err);
166
+ throw new Error(this.redact(msg));
167
+ }
168
+ }
169
+ async cloneRegistry(targetDir) {
170
+ try {
171
+ execFileSync("git", ["clone", "--depth", "1", this.cloneUrl(), targetDir], { stdio: ["pipe", "pipe", "pipe"] });
172
+ } catch (err) {
173
+ const msg = err instanceof Error ? err.message : String(err);
174
+ throw new Error(this.redact(msg));
175
+ }
176
+ this.git(["config", "user.name", "vibe-publish[bot]"], targetDir);
177
+ this.git(["config", "user.email", `vibe-publish[bot]@users.noreply.${this.hostname}`], targetDir);
178
+ return targetDir;
179
+ }
180
+ async createPullRequest(opts) {
181
+ const { repoDir, branchName, title, body, baseBranch, filesToAdd } = opts;
182
+ this.git(["checkout", "-b", branchName], repoDir);
183
+ for (const file of filesToAdd) {
184
+ this.git(["add", file], repoDir);
185
+ }
186
+ this.git(["commit", "-m", title], repoDir);
187
+ this.git(["push", "origin", branchName], repoDir);
188
+ const apiUrl = `${this.apiUrl}/repos/${this.repo}/pulls`;
189
+ const response = await fetch(apiUrl, {
190
+ method: "POST",
191
+ headers: {
192
+ Authorization: `Bearer ${this.token}`,
193
+ Accept: "application/vnd.github+json",
194
+ "Content-Type": "application/json"
195
+ },
196
+ body: JSON.stringify({
197
+ title,
198
+ body,
199
+ head: branchName,
200
+ base: baseBranch
201
+ })
202
+ });
203
+ if (!response.ok) {
204
+ const text = await response.text();
205
+ throw new Error(`Failed to create PR: ${response.status} ${text}`);
206
+ }
207
+ const pr = await response.json();
208
+ return { url: pr.html_url, number: pr.number };
209
+ }
210
+ async pushDirectly(opts) {
211
+ const { repoDir, branchName, commitMessage, filesToAdd } = opts;
212
+ for (const file of filesToAdd) {
213
+ this.git(["add", file], repoDir);
214
+ }
215
+ this.git(["commit", "-m", commitMessage], repoDir);
216
+ this.git(["push", "origin", branchName], repoDir);
217
+ }
218
+ }
219
+
220
+ // src/git-platform/index.ts
221
+ function resolveGitPlatform(config) {
222
+ switch (config.gitPlatform) {
223
+ case "github":
224
+ if (!config.registryRepo || !config.registryToken) {
225
+ throw new Error("GitHub requires VIBE_REGISTRY_REPO and VIBE_REGISTRY_TOKEN");
226
+ }
227
+ return new GitHubAdapter({
228
+ registryRepo: config.registryRepo,
229
+ token: config.registryToken,
230
+ apiUrl: config.githubApiUrl,
231
+ serverUrl: config.githubServerUrl
232
+ });
233
+ case "azure-devops":
234
+ throw new Error('Azure DevOps adapter is not yet implemented. Set VIBE_GIT_PLATFORM="github" or implement AzureDevOpsAdapter.');
235
+ case "gitlab":
236
+ throw new Error('GitLab adapter is not yet implemented. Set VIBE_GIT_PLATFORM="github" or implement GitLabAdapter.');
237
+ default:
238
+ throw new Error(`Unknown git platform: "${config.gitPlatform}". Supported: github, azure-devops, gitlab`);
239
+ }
240
+ }
241
+
242
+ // src/registry.ts
243
+ import { readFileSync as readFileSync2, writeFileSync, mkdirSync } from "node:fs";
244
+ import { join } from "node:path";
245
+ import { tmpdir } from "node:os";
246
+ import { randomBytes } from "node:crypto";
247
+ async function updateRegistry(opts) {
248
+ const { entry, environment, directPush, git } = opts;
249
+ const tmpBase = join(tmpdir(), `vibe-registry-${randomBytes(4).toString("hex")}`);
250
+ mkdirSync(tmpBase, { recursive: true });
251
+ const repoDir = await git.cloneRegistry(tmpBase);
252
+ const appsFile = join(repoDir, environment, "apps.json");
253
+ let apps;
254
+ try {
255
+ apps = JSON.parse(readFileSync2(appsFile, "utf-8"));
256
+ } catch {
257
+ apps = [];
258
+ }
259
+ const existingIdx = apps.findIndex((a) => a.id === entry.id);
260
+ if (existingIdx !== -1) {
261
+ apps[existingIdx] = entry;
262
+ } else {
263
+ apps.push(entry);
264
+ }
265
+ apps.sort((a, b) => a.id.localeCompare(b.id));
266
+ writeFileSync(appsFile, JSON.stringify(apps, null, 2) + `
267
+ `);
268
+ const relPath = `${environment}/apps.json`;
269
+ if (directPush) {
270
+ await git.pushDirectly({
271
+ repoDir,
272
+ branchName: "main",
273
+ commitMessage: `publish: ${entry.id} v${entry.version} → ${environment}`,
274
+ filesToAdd: [relPath]
275
+ });
276
+ return { method: "push" };
277
+ }
278
+ const branchName = `publish/${environment}/${entry.id}-${entry.version}`;
279
+ const pr = await git.createPullRequest({
280
+ repoDir,
281
+ branchName,
282
+ title: `publish: ${entry.id} v${entry.version} → ${environment}`,
283
+ body: [
284
+ `## Publish ${entry.id} v${entry.version}`,
285
+ "",
286
+ `| Field | Value |`,
287
+ `|---|---|`,
288
+ `| **Environment** | ${environment} |`,
289
+ `| **Bundle URL** | ${entry.bundleUrl} |`,
290
+ `| **Integrity** | \`${entry.integrity.slice(0, 20)}…\` |`,
291
+ `| **Published by** | ${entry.publishedBy} |`
292
+ ].join(`
293
+ `),
294
+ baseBranch: "main",
295
+ filesToAdd: [relPath]
296
+ });
297
+ return { method: "pr", url: pr.url };
298
+ }
299
+
300
+ // src/publish.ts
301
+ async function publish(appDir, config) {
302
+ const manifestPath = resolve2(appDir, "manifest.json");
303
+ const bundlePath = resolve2(appDir, "dist/index.js");
304
+ let manifest;
305
+ try {
306
+ manifest = JSON.parse(readFileSync3(manifestPath, "utf-8"));
307
+ } catch {
308
+ throw new Error(`Cannot read manifest.json at ${manifestPath}`);
309
+ }
310
+ let bundleContent;
311
+ try {
312
+ bundleContent = readFileSync3(bundlePath);
313
+ } catch {
314
+ throw new Error(`Cannot read dist/index.js at ${bundlePath}. Did you run "bun run build"?`);
315
+ }
316
+ const { id, version } = manifest;
317
+ console.log(`Publishing ${id} v${version} → ${config.environment}`);
318
+ const integrity = computeIntegrity(bundleContent);
319
+ const storage = resolveStorageProvider(config);
320
+ const alreadyExists = await storage.exists(id, version, "index.js");
321
+ if (alreadyExists) {
322
+ throw new Error(`Immutability violation: ${id}/${version}/index.js already exists in storage. ` + `Bump the version in manifest.json to publish a new release.`);
323
+ }
324
+ console.log(`Uploading ${id}/${version}/index.js…`);
325
+ const { url: bundleUrl, sizeBytes } = await storage.upload(id, version, "index.js", bundleContent, "application/javascript");
326
+ console.log(`Uploaded: ${bundleUrl} (${sizeBytes} bytes)`);
327
+ const entry = {
328
+ ...manifest,
329
+ bundleUrl,
330
+ integrity,
331
+ bundleSizeBytes: sizeBytes,
332
+ publishedAt: new Date().toISOString(),
333
+ publishedBy: config.publishedBy
334
+ };
335
+ console.log(`Updating registry (${config.environment})…`);
336
+ const git = resolveGitPlatform(config);
337
+ const result = await updateRegistry({
338
+ entry,
339
+ environment: config.environment,
340
+ directPush: config.directPush,
341
+ git
342
+ });
343
+ if (result.method === "pr") {
344
+ console.log(`PR created: ${result.url}`);
345
+ } else {
346
+ console.log(`Pushed directly to main`);
347
+ }
348
+ return {
349
+ entry,
350
+ registryMethod: result.method,
351
+ prUrl: result.url
352
+ };
353
+ }
354
+
355
+ // src/index.ts
356
+ async function main() {
357
+ const { values } = parseArgs({
358
+ options: {
359
+ environment: { type: "string", short: "e" },
360
+ "direct-push": { type: "boolean", default: false },
361
+ "app-dir": { type: "string", default: "." },
362
+ help: { type: "boolean", short: "h" }
363
+ }
364
+ });
365
+ if (values.help) {
366
+ console.log(`
367
+ Usage: vibe-publish [options]
368
+
369
+ Options:
370
+ -e, --environment <env> Target environment: dev, staging, prod (default: dev)
371
+ --direct-push Push directly to main instead of opening a PR
372
+ --app-dir <path> Path to the mini-app directory (default: .)
373
+ -h, --help Show this help
374
+ `.trim());
375
+ process.exit(0);
376
+ }
377
+ const config = loadConfig({
378
+ environment: values.environment,
379
+ directPush: values["direct-push"] || undefined
380
+ });
381
+ const appDir = values["app-dir"] ?? process.cwd();
382
+ try {
383
+ const result = await publish(appDir, config);
384
+ console.log(`
385
+ Done. ${result.entry.id} v${result.entry.version} published.`);
386
+ } catch (err) {
387
+ console.error(`
388
+ Publish failed: ${err.message}`);
389
+ process.exit(1);
390
+ }
391
+ }
392
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiworkbench/vibe-publish",
3
- "version": "0.0.2",
3
+ "version": "0.0.5",
4
4
  "publishConfig": { "access": "public" },
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,6 @@
9
9
  "main": "dist/index.js",
10
10
  "exports": {
11
11
  ".": {
12
- "bun": "./src/index.ts",
13
12
  "import": "./dist/index.js"
14
13
  }
15
14
  },
@@ -18,15 +17,16 @@
18
17
  "dev": "bun --watch src/index.ts",
19
18
  "test": "bun test",
20
19
  "type-check": "tsc --noEmit",
21
- "clean": "rm -rf dist"
20
+ "clean": "rm -rf dist",
21
+ "prepublishOnly": "bun run build"
22
22
  },
23
23
  "dependencies": {
24
- "@aiworkbench/vibe-types": "^0.0.2",
24
+ "@aiworkbench/vibe-types": "^0.0.4",
25
25
  "@azure/storage-blob": "^12.25.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.0.0",
29
29
  "typescript": "^5.7.0"
30
30
  },
31
- "files": ["dist", "src"]
31
+ "files": ["dist"]
32
32
  }
@@ -1,87 +0,0 @@
1
- import { describe, test, expect, beforeEach, afterEach } from "bun:test";
2
- import { loadConfig } from "../config.js";
3
-
4
- describe("loadConfig — GitHub URL resolution", () => {
5
- const envBackup: Record<string, string | undefined> = {};
6
- const envKeys = [
7
- "VIBE_GITHUB_API_URL",
8
- "VIBE_GITHUB_SERVER_URL",
9
- "GITHUB_API_URL",
10
- "GITHUB_SERVER_URL",
11
- ];
12
-
13
- beforeEach(() => {
14
- for (const key of envKeys) {
15
- envBackup[key] = process.env[key];
16
- delete process.env[key];
17
- }
18
- });
19
-
20
- afterEach(() => {
21
- for (const key of envKeys) {
22
- if (envBackup[key] === undefined) {
23
- delete process.env[key];
24
- } else {
25
- process.env[key] = envBackup[key];
26
- }
27
- }
28
- });
29
-
30
- test("defaults to public GitHub URLs when no env vars set", () => {
31
- const config = loadConfig();
32
- expect(config.githubApiUrl).toBe("https://api.github.com");
33
- expect(config.githubServerUrl).toBe("https://github.com");
34
- });
35
-
36
- test("VIBE_GITHUB_* env vars override defaults", () => {
37
- process.env.VIBE_GITHUB_API_URL = "https://ghe.corp.com/api/v3";
38
- process.env.VIBE_GITHUB_SERVER_URL = "https://ghe.corp.com";
39
-
40
- const config = loadConfig();
41
- expect(config.githubApiUrl).toBe("https://ghe.corp.com/api/v3");
42
- expect(config.githubServerUrl).toBe("https://ghe.corp.com");
43
- });
44
-
45
- test("GITHUB_* env vars (CI-provided) override defaults", () => {
46
- process.env.GITHUB_API_URL = "https://ghe.corp.com/api/v3";
47
- process.env.GITHUB_SERVER_URL = "https://ghe.corp.com";
48
-
49
- const config = loadConfig();
50
- expect(config.githubApiUrl).toBe("https://ghe.corp.com/api/v3");
51
- expect(config.githubServerUrl).toBe("https://ghe.corp.com");
52
- });
53
-
54
- test("VIBE_GITHUB_* takes precedence over GITHUB_*", () => {
55
- process.env.VIBE_GITHUB_API_URL = "https://vibe-override/api/v3";
56
- process.env.GITHUB_API_URL = "https://ci-provided/api/v3";
57
-
58
- const config = loadConfig();
59
- expect(config.githubApiUrl).toBe("https://vibe-override/api/v3");
60
- });
61
-
62
- test("explicit overrides take precedence over all env vars", () => {
63
- process.env.VIBE_GITHUB_API_URL = "https://env-value/api/v3";
64
-
65
- const config = loadConfig({
66
- githubApiUrl: "https://explicit-override/api/v3",
67
- });
68
- expect(config.githubApiUrl).toBe("https://explicit-override/api/v3");
69
- });
70
-
71
- test("empty string env vars are treated as unset", () => {
72
- process.env.VIBE_GITHUB_API_URL = "";
73
- process.env.VIBE_GITHUB_SERVER_URL = "";
74
-
75
- const config = loadConfig();
76
- expect(config.githubApiUrl).toBe("https://api.github.com");
77
- expect(config.githubServerUrl).toBe("https://github.com");
78
- });
79
-
80
- test("empty VIBE_GITHUB_* falls through to GITHUB_*", () => {
81
- process.env.VIBE_GITHUB_API_URL = "";
82
- process.env.GITHUB_API_URL = "https://ghe.corp.com/api/v3";
83
-
84
- const config = loadConfig();
85
- expect(config.githubApiUrl).toBe("https://ghe.corp.com/api/v3");
86
- });
87
- });
@@ -1,57 +0,0 @@
1
- import { describe, test, expect } from "bun:test";
2
- import { GitHubAdapter, type GitHubConfig } from "../git-platform/github.js";
3
-
4
- function createAdapter(overrides: Partial<GitHubConfig> = {}): GitHubAdapter {
5
- return new GitHubAdapter({
6
- registryRepo: "my-org/vibe-registry",
7
- token: "ghp_test123",
8
- apiUrl: "https://api.github.com",
9
- serverUrl: "https://github.com",
10
- ...overrides,
11
- });
12
- }
13
-
14
- describe("GitHubAdapter — public GitHub", () => {
15
- const adapter = createAdapter();
16
-
17
- test("cloneUrl uses github.com", () => {
18
- // Access private method via prototype for testing
19
- const url = (adapter as any).cloneUrl();
20
- expect(url).toBe(
21
- "https://x-access-token:ghp_test123@github.com/my-org/vibe-registry.git",
22
- );
23
- });
24
-
25
- test("hostname is derived from serverUrl", () => {
26
- expect((adapter as any).hostname).toBe("github.com");
27
- });
28
- });
29
-
30
- describe("GitHubAdapter — GitHub Enterprise Server", () => {
31
- const adapter = createAdapter({
32
- apiUrl: "https://ghe.corp.com/api/v3",
33
- serverUrl: "https://ghe.corp.com",
34
- });
35
-
36
- test("cloneUrl uses GHE hostname", () => {
37
- const url = (adapter as any).cloneUrl();
38
- expect(url).toBe(
39
- "https://x-access-token:ghp_test123@ghe.corp.com/my-org/vibe-registry.git",
40
- );
41
- });
42
-
43
- test("hostname is derived from GHE serverUrl", () => {
44
- expect((adapter as any).hostname).toBe("ghe.corp.com");
45
- });
46
- });
47
-
48
- describe("GitHubAdapter — GHE with custom port", () => {
49
- const adapter = createAdapter({
50
- apiUrl: "https://ghe.corp.com:8443/api/v3",
51
- serverUrl: "https://ghe.corp.com:8443",
52
- });
53
-
54
- test("hostname does not include port", () => {
55
- expect((adapter as any).hostname).toBe("ghe.corp.com");
56
- });
57
- });
package/src/config.ts DELETED
@@ -1,72 +0,0 @@
1
- export type Environment = "dev" | "staging" | "prod";
2
- export type StorageProviderType = "azure-blob" | "s3" | "gcs";
3
- export type GitPlatformType = "github" | "azure-devops" | "gitlab";
4
-
5
- export interface PublishConfig {
6
- environment: Environment;
7
- directPush: boolean;
8
-
9
- // Storage
10
- storageProvider: StorageProviderType;
11
- azureAccountName?: string;
12
- azureAccountKey?: string;
13
- azureContainerName?: string;
14
-
15
- // Git platform
16
- gitPlatform: GitPlatformType;
17
- registryRepo?: string;
18
- registryToken?: string;
19
-
20
- // GitHub host URLs (for GitHub Enterprise Server support)
21
- githubApiUrl: string;
22
- githubServerUrl: string;
23
-
24
- // Publisher identity
25
- publishedBy: string;
26
- }
27
-
28
- export function loadConfig(overrides: Partial<PublishConfig> = {}): PublishConfig {
29
- const env = (overrides.environment ??
30
- process.env.VIBE_ENVIRONMENT ??
31
- "dev") as Environment;
32
-
33
- return {
34
- environment: env,
35
- directPush:
36
- overrides.directPush ?? process.env.VIBE_DIRECT_PUSH === "true",
37
-
38
- storageProvider: (overrides.storageProvider ??
39
- process.env.VIBE_STORAGE_PROVIDER ??
40
- "azure-blob") as StorageProviderType,
41
- azureAccountName:
42
- overrides.azureAccountName ?? process.env.VIBE_STORAGE_ACCOUNT_NAME,
43
- azureAccountKey:
44
- overrides.azureAccountKey ?? process.env.VIBE_STORAGE_ACCOUNT_KEY,
45
- azureContainerName:
46
- overrides.azureContainerName ?? process.env.VIBE_STORAGE_CONTAINER_NAME,
47
-
48
- gitPlatform: (overrides.gitPlatform ??
49
- process.env.VIBE_GIT_PLATFORM ??
50
- "github") as GitPlatformType,
51
- registryRepo: overrides.registryRepo ?? process.env.VIBE_REGISTRY_REPO,
52
- registryToken: overrides.registryToken ?? process.env.VIBE_REGISTRY_TOKEN,
53
-
54
- githubApiUrl:
55
- overrides.githubApiUrl ??
56
- (process.env.VIBE_GITHUB_API_URL || undefined) ??
57
- (process.env.GITHUB_API_URL || undefined) ??
58
- "https://api.github.com",
59
-
60
- githubServerUrl:
61
- overrides.githubServerUrl ??
62
- (process.env.VIBE_GITHUB_SERVER_URL || undefined) ??
63
- (process.env.GITHUB_SERVER_URL || undefined) ??
64
- "https://github.com",
65
-
66
- publishedBy:
67
- overrides.publishedBy ??
68
- process.env.GITHUB_ACTOR ??
69
- process.env.USER ??
70
- "unknown",
71
- };
72
- }
@@ -1,39 +0,0 @@
1
- import type { GitPlatformAdapter } from "./interface.js";
2
-
3
- /**
4
- * Azure DevOps Git adapter — stub.
5
- *
6
- * To implement: use the Azure DevOps REST API or `az repos` CLI
7
- * for clone, push, and PR creation.
8
- */
9
- export class AzureDevOpsAdapter implements GitPlatformAdapter {
10
- constructor(_config: { organization: string; project: string; repo: string; token: string }) {
11
- throw new Error(
12
- "AzureDevOpsAdapter is not yet implemented. See azure-devops.ts for notes.",
13
- );
14
- }
15
-
16
- cloneRegistry(_targetDir: string): Promise<string> {
17
- throw new Error("Not implemented");
18
- }
19
-
20
- createPullRequest(_opts: {
21
- repoDir: string;
22
- branchName: string;
23
- title: string;
24
- body: string;
25
- baseBranch: string;
26
- filesToAdd: string[];
27
- }): Promise<{ url: string; number: number }> {
28
- throw new Error("Not implemented");
29
- }
30
-
31
- pushDirectly(_opts: {
32
- repoDir: string;
33
- branchName: string;
34
- commitMessage: string;
35
- filesToAdd: string[];
36
- }): Promise<void> {
37
- throw new Error("Not implemented");
38
- }
39
- }