deploylog 0.2.1 → 0.2.2

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/api.js CHANGED
@@ -23,11 +23,25 @@ async function request(path, options = {}) {
23
23
  ...options.headers,
24
24
  },
25
25
  });
26
- const json = await res.json();
26
+ const raw = await res.text();
27
+ let body = null;
28
+ if (raw) {
29
+ try {
30
+ body = JSON.parse(raw);
31
+ }
32
+ catch {
33
+ body = null;
34
+ }
35
+ }
27
36
  if (!res.ok) {
28
- throw new ApiError(res.status, json.error?.code ?? 'UNKNOWN', json.error?.message ?? `Request failed (${res.status})`);
37
+ throw new ApiError(res.status, body?.error?.code ?? 'UNKNOWN', body?.error?.message ?? `Request failed (${res.status})`);
38
+ }
39
+ // Reject non-JSON bodies and well-formed JSON that's missing `data` — both
40
+ // violate the response contract and would otherwise return undefined.
41
+ if (!body || body.data === undefined) {
42
+ throw new ApiError(res.status, 'INVALID_RESPONSE', `Server returned an unexpected response (${res.status})`);
29
43
  }
30
- return json.data;
44
+ return body.data;
31
45
  }
32
46
  export async function listProjects() {
33
47
  return request('/projects');
package/dist/config.js CHANGED
@@ -13,10 +13,12 @@ export function setApiKey(key) {
13
13
  config.set('apiKey', key);
14
14
  }
15
15
  export function getApiUrl() {
16
- return config.get('apiUrl') ?? 'https://deploylog.dev';
16
+ return (config.get('apiUrl') ?? 'https://deploylog.dev').replace(/\/+$/, '');
17
17
  }
18
18
  export function setApiUrl(url) {
19
- config.set('apiUrl', url);
19
+ // Normalize so request building (`${apiUrl}/api/cli${path}`) never produces a
20
+ // double slash from a trailing-slash paste. (BUG-012)
21
+ config.set('apiUrl', url.trim().replace(/\/+$/, ''));
20
22
  }
21
23
  export function clearConfig() {
22
24
  config.clear();
package/dist/index.js CHANGED
@@ -18,6 +18,20 @@ program
18
18
  .option('--api-url <url>', 'API base URL (default: https://deploylog.dev)')
19
19
  .action(async (opts) => {
20
20
  if (opts.apiUrl) {
21
+ let parsed;
22
+ try {
23
+ parsed = new URL(opts.apiUrl.trim());
24
+ }
25
+ catch {
26
+ console.error(chalk.red('Invalid API URL. Provide a valid absolute URL.'));
27
+ process.exit(1);
28
+ }
29
+ // Require http(s) and a real host — a scheme-only value like "https://"
30
+ // parses but would break request URL construction.
31
+ if (!/^https?:$/.test(parsed.protocol) || !parsed.hostname) {
32
+ console.error(chalk.red('Invalid API URL. Must use http(s) and include a host.'));
33
+ process.exit(1);
34
+ }
21
35
  setApiUrl(opts.apiUrl);
22
36
  }
23
37
  if (opts.key) {
@@ -175,11 +189,19 @@ program
175
189
  return;
176
190
  }
177
191
  }
192
+ else if (!opts.yes) {
193
+ // Non-interactive shell (CI, piped stdin): there's no prompt to show,
194
+ // so make the unreviewed auto-proceed explicit. (BUG-019)
195
+ console.log(chalk.yellow('Non-interactive shell: proceeding with the AI-generated entry without confirmation.'));
196
+ }
178
197
  }
179
198
  if (!title || !body) {
180
199
  console.error(chalk.red('Entry requires --title and --body (or --from-git / --ai-summarize to derive them).'));
181
200
  process.exit(1);
182
201
  }
202
+ if (opts.publish && opts.draft) {
203
+ console.log(chalk.yellow('Both --publish and --draft passed; saving as a draft.'));
204
+ }
183
205
  const entry = await createEntry(slug, {
184
206
  title,
185
207
  body_markdown: body,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploylog",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Push changelog entries from the terminal",
5
5
  "type": "module",
6
6
  "bin": {