2026-07-22 15:03:32 +00:00
|
|
|
/**
|
|
|
|
|
* Forgejo API client.
|
|
|
|
|
*
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
* A thin wrapper around the Forgejo REST API (`/api/v1`). It supports two
|
|
|
|
|
* authentication modes:
|
|
|
|
|
* - Basic auth (username/password) — required by Forgejo for the personal
|
|
|
|
|
* access token endpoints (create/delete).
|
|
|
|
|
* - Token auth — used for every other call.
|
|
|
|
|
*
|
|
|
|
|
* Each public method maps to a single endpoint; see the README for the
|
|
|
|
|
* command-to-endpoint mapping.
|
2026-07-22 15:03:32 +00:00
|
|
|
*/
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
|
|
|
|
|
const pkg = require('../package.json');
|
|
|
|
|
|
|
|
|
|
const REQUEST_TIMEOUT_MS = 30000;
|
|
|
|
|
// Repository migrations clone the full source repository and can legitimately
|
|
|
|
|
// take minutes, so they get a much longer budget.
|
|
|
|
|
const MIGRATE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
|
|
|
|
2026-07-22 15:03:32 +00:00
|
|
|
class ForgejoClient {
|
|
|
|
|
constructor(baseUrl, token = null) {
|
|
|
|
|
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
|
|
|
this.token = token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static fromConfig(config) {
|
|
|
|
|
if (!config || !config.url || !config.token) {
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
throw new Error('Not authenticated. Run: stoke auth login');
|
2026-07-22 15:03:32 +00:00
|
|
|
}
|
|
|
|
|
return new ForgejoClient(config.url, config.token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
withBasicAuth(username, password) {
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
const clone = new ForgejoClient(this.baseUrl);
|
2026-07-22 15:03:32 +00:00
|
|
|
clone.basicAuth = Buffer.from(`${username}:${password}`).toString('base64');
|
|
|
|
|
return clone;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
headers(extra = {}) {
|
|
|
|
|
const h = {
|
|
|
|
|
Accept: 'application/json',
|
|
|
|
|
'Content-Type': 'application/json',
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
'User-Agent': `stoke/${pkg.version}`,
|
2026-07-22 15:03:32 +00:00
|
|
|
...extra,
|
|
|
|
|
};
|
|
|
|
|
if (this.token) {
|
|
|
|
|
h.Authorization = `token ${this.token}`;
|
|
|
|
|
} else if (this.basicAuth) {
|
|
|
|
|
h.Authorization = `Basic ${this.basicAuth}`;
|
|
|
|
|
}
|
|
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
async request(method, endpoint, body = null, { timeout = REQUEST_TIMEOUT_MS } = {}) {
|
2026-07-22 15:03:32 +00:00
|
|
|
const url = `${this.baseUrl}/api/v1${endpoint}`;
|
|
|
|
|
const opts = {
|
|
|
|
|
method,
|
|
|
|
|
headers: this.headers(),
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
signal: AbortSignal.timeout(timeout),
|
2026-07-22 15:03:32 +00:00
|
|
|
};
|
|
|
|
|
if (body !== null) {
|
|
|
|
|
opts.body = JSON.stringify(body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let res;
|
|
|
|
|
try {
|
|
|
|
|
res = await fetch(url, opts);
|
|
|
|
|
} catch (err) {
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
if (err.name === 'TimeoutError') {
|
|
|
|
|
throw new Error(`Request to ${this.baseUrl} timed out after ${timeout / 1000}s`);
|
|
|
|
|
}
|
2026-07-22 15:03:32 +00:00
|
|
|
throw new Error(`Network error reaching ${this.baseUrl}: ${err.message}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const text = await res.text();
|
|
|
|
|
let data = null;
|
|
|
|
|
if (text) {
|
|
|
|
|
try {
|
|
|
|
|
data = JSON.parse(text);
|
|
|
|
|
} catch {
|
|
|
|
|
data = { raw: text };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
const msg = data?.message || data?.raw || `HTTP ${res.status}`;
|
|
|
|
|
const err = new Error(msg);
|
|
|
|
|
err.status = res.status;
|
|
|
|
|
err.body = data;
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get(endpoint) {
|
|
|
|
|
return this.request('GET', endpoint);
|
|
|
|
|
}
|
|
|
|
|
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
post(endpoint, body, opts) {
|
|
|
|
|
return this.request('POST', endpoint, body, opts);
|
2026-07-22 15:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
del(endpoint) {
|
|
|
|
|
return this.request('DELETE', endpoint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async verifyBasicAuth(username, password) {
|
|
|
|
|
const client = this.withBasicAuth(username, password);
|
|
|
|
|
return client.get('/user');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createToken(username, password, name, scopes) {
|
|
|
|
|
const client = this.withBasicAuth(username, password);
|
|
|
|
|
return client.post(`/users/${encodeURIComponent(username)}/tokens`, { name, scopes });
|
|
|
|
|
}
|
|
|
|
|
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
// Forgejo requires Basic auth for token management endpoints; a token
|
|
|
|
|
// cannot be used to revoke itself (the API answers 401).
|
|
|
|
|
async deleteToken(username, password, login, id) {
|
|
|
|
|
const client = this.withBasicAuth(username, password);
|
|
|
|
|
return client.del(`/users/${encodeURIComponent(login)}/tokens/${id}`);
|
2026-07-22 15:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createRepo(payload) {
|
|
|
|
|
return this.post('/user/repos', payload);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async migrateRepo(payload) {
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
return this.post('/repos/migrate', payload, { timeout: MIGRATE_TIMEOUT_MS });
|
2026-07-22 15:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getAll(endpoint, params = {}) {
|
|
|
|
|
const pageSize = 50;
|
|
|
|
|
const all = [];
|
|
|
|
|
for (let page = 1; page <= 1000; page += 1) {
|
|
|
|
|
const query = new URLSearchParams({ ...params, limit: String(pageSize), page: String(page) }).toString();
|
|
|
|
|
const items = await this.get(`${endpoint}?${query}`);
|
|
|
|
|
if (!Array.isArray(items) || items.length === 0) break;
|
|
|
|
|
all.push(...items);
|
|
|
|
|
if (items.length < pageSize) break;
|
|
|
|
|
}
|
|
|
|
|
return all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async listRepos(opts = {}) {
|
|
|
|
|
return this.getAll('/user/repos', opts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async listIssues(owner, repo, opts = {}) {
|
|
|
|
|
const params = {};
|
|
|
|
|
if (opts.state) params.state = opts.state;
|
|
|
|
|
if (opts.type) params.type = opts.type;
|
|
|
|
|
return this.getAll(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues`, params);
|
|
|
|
|
}
|
|
|
|
|
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
async createIssue(owner, repo, payload) {
|
|
|
|
|
return this.post(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues`, payload);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-22 15:03:32 +00:00
|
|
|
async listPullRequests(owner, repo, opts = {}) {
|
|
|
|
|
return this.getAll(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls`, opts);
|
|
|
|
|
}
|
|
|
|
|
|
Audit: fix auth/config bugs, add issue/pr create, tests and docs
Fixes found during a full audit of the CLI:
- auth logout: remote token revocation always failed with 401 because
Forgejo only accepts Basic auth on the token endpoints. Logout now
asks for (or accepts) the account password, supports --password,
--password-file and --local-only, and clearly reports when the token
is left active.
- Silent password prompt actually echoed the password on a TTY:
overriding rl.write does not suppress readline echo. Switched to the
callback readline module and mute _writeToOutput instead (the
readline/promises interface does not honor that hook).
- Global --config flag was silently ignored: config paths were resolved
at require time, before the preAction hook set STOKE_CONFIG_FILE.
Paths are now resolved lazily on every access.
- XDG_CONFIG_HOME handling put the config in $XDG_CONFIG_HOME/.config/stoke;
per the XDG spec it now resolves to $XDG_CONFIG_HOME/stoke.
- repo create: --auto-init defaulted to true with no way to disable it;
added --no-auto-init.
- repo import/import-batch: a GitHub token was required even for
non-GitHub services (e.g. --service git), making those imports fail
without gh/GITHUB_TOKEN. Tokens are now only auto-resolved for the
github service; batch imports resolve per entry and memoize.
- Branding leftovers: 'Run: forgejo auth login' hint and
forgejo-cli/1.0.0 User-Agent now say stoke (UA tracks pkg.version).
- Added request timeouts (30s default, 10m for migrations).
- --limit and --team-id are validated as integers instead of silently
misbehaving on garbage (NaN made -l show all results).
New commands (per the repo's every-operation-becomes-a-command design):
- stoke issue create (title/body/body-file/assignees)
- stoke pr create (head/base/title/body/body-file)
Tests and metadata:
- New test suite on the built-in node:test runner (25 tests) covering
config resolution/persistence, the API client with a mocked fetch,
and end-to-end CLI behavior. npm test previously matched no files.
- package.json: engines >=22.12.0 (required by commander@15 — the
README claimed Node 18), repository, keywords, author; version 1.1.0.
- README: corrected Node requirement, documented repo rename (was
missing), issue create, pr create, logout options and revocation
caveat, --no-auto-init, XDG behavior, import token rules, testing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:28:23 +00:00
|
|
|
async createPullRequest(owner, repo, payload) {
|
|
|
|
|
return this.post(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls`, payload);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-22 21:19:36 +00:00
|
|
|
async getPullRequest(owner, repo, index) {
|
|
|
|
|
return this.get(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${index}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createPullRequestComment(owner, repo, index, body) {
|
|
|
|
|
return this.post(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${index}/comments`, { body });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createPullRequestReview(owner, repo, index, event, body) {
|
|
|
|
|
return this.post(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${index}/reviews`, {
|
|
|
|
|
event,
|
|
|
|
|
body: body || '',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
Add apt distribution: deb packaging, registry publish, docs (#1)
Implements #1 — stoke installable with apt-get install stoke.
Packaging:
- scripts/build-deb.sh: builds dist/stoke_<version>_all.deb from a clean
staging copy (src + fresh npm ci --omit=dev), pure-JS Architecture: all,
Depends: nodejs (>= 22.12), /usr/lib/stoke payload with /usr/bin/stoke
symlink, copyright + changelog, normalized permissions. Lintian-clean.
- scripts/publish-deb.sh: uploads a .deb to the Forgejo Debian registry
(owner/distribution/component parameterized, defaults heavy-duty/
stable/main), authenticating with STOKE_TOKEN or the stoke login token.
- scripts/install-apt.sh: consumer-side one-time setup — adds the
registry key and apt source, then apt-get install stoke. Falls back to
a [trusted=yes] source when apt's sqv verifier rejects the forge's
registry signature (known upstream Forgejo signing bug; the script
prefers the signed source so setups heal once the forge is fixed).
- .forgejo/workflows/release.yml: on v* tags — test, build, publish to
the heavy-duty registry, attach the .deb to the release page. Needs a
runner and a RELEASE_TOKEN secret with org package write.
New command:
- stoke pr merge (-n, --method merge|rebase|rebase-merge|squash,
--title, --message, --delete-branch) — gap found while merging !2.
Docs and housekeeping:
- README: 'Install with apt' as the primary installation method with
manual setup and dpkg fallback, signature caveat, pr merge reference,
Packaging and releasing section with a release checklist.
- dist/ gitignored; version bumped to 1.2.0.
Verified end-to-end on this machine: built the deb (lintian-clean),
published it to the Forgejo Debian registry, installed it with
apt-get install stoke via install-apt.sh, and confirmed the installed
CLI works against the live forge. The test upload was removed from the
personal namespace afterwards; publishing under heavy-duty needs an
org-member token (401 reqPackageAccess with this restricted account).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:40:51 +00:00
|
|
|
async mergePullRequest(owner, repo, index, payload) {
|
|
|
|
|
return this.post(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${index}/merge`, payload);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-22 15:03:32 +00:00
|
|
|
async listBranches(owner, repo, opts = {}) {
|
|
|
|
|
return this.getAll(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branches`, opts);
|
|
|
|
|
}
|
2026-07-22 15:06:35 +00:00
|
|
|
|
|
|
|
|
async addCollaborator(owner, repo, username, permission) {
|
|
|
|
|
return this.request('PUT', `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/collaborators/${encodeURIComponent(username)}`, {
|
|
|
|
|
permission,
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-07-22 15:16:42 +00:00
|
|
|
|
|
|
|
|
async renameRepo(owner, repo, newName) {
|
|
|
|
|
return this.request('PATCH', `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`, {
|
|
|
|
|
name: newName,
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-07-22 17:47:48 +00:00
|
|
|
|
|
|
|
|
async transferRepo(owner, repo, newOwner) {
|
|
|
|
|
return this.post(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/transfer`, {
|
|
|
|
|
new_owner: newOwner,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createOrg(payload) {
|
|
|
|
|
return this.post('/orgs', payload);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async listOrgRepos(org, opts = {}) {
|
|
|
|
|
return this.getAll(`/orgs/${encodeURIComponent(org)}/repos`, opts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async updateOrgAvatar(org, base64Image) {
|
|
|
|
|
return this.post(`/orgs/${encodeURIComponent(org)}/avatar`, {
|
|
|
|
|
image: base64Image,
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-07-22 17:56:14 +00:00
|
|
|
|
|
|
|
|
async searchUsers(query = '', opts = {}) {
|
|
|
|
|
const pageSize = 50;
|
|
|
|
|
const all = [];
|
|
|
|
|
for (let page = 1; page <= 1000; page += 1) {
|
|
|
|
|
const queryString = new URLSearchParams({
|
|
|
|
|
q: query,
|
|
|
|
|
...opts,
|
|
|
|
|
limit: String(pageSize),
|
|
|
|
|
page: String(page),
|
|
|
|
|
}).toString();
|
|
|
|
|
const res = await this.get(`/users/search?${queryString}`);
|
|
|
|
|
const items = Array.isArray(res?.data) ? res.data : [];
|
|
|
|
|
if (items.length === 0) break;
|
|
|
|
|
all.push(...items);
|
|
|
|
|
if (items.length < pageSize) break;
|
|
|
|
|
}
|
|
|
|
|
return all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async listOrgTeams(org, opts = {}) {
|
|
|
|
|
return this.getAll(`/orgs/${encodeURIComponent(org)}/teams`, opts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createTeam(org, payload) {
|
|
|
|
|
return this.post(`/orgs/${encodeURIComponent(org)}/teams`, payload);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async addTeamMember(teamId, username) {
|
|
|
|
|
return this.request('PUT', `/teams/${teamId}/members/${encodeURIComponent(username)}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async listTeamMembers(teamId, opts = {}) {
|
|
|
|
|
return this.getAll(`/teams/${teamId}/members`, opts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async removeTeamMember(teamId, username) {
|
|
|
|
|
return this.del(`/teams/${teamId}/members/${encodeURIComponent(username)}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getUser(username) {
|
|
|
|
|
return this.get(`/users/${encodeURIComponent(username)}`);
|
|
|
|
|
}
|
2026-07-22 15:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = { ForgejoClient };
|