@opencode-linear-agent/server 0.1.3-master.15 → 0.1.3-master.17
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 +128 -131
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -66902,7 +66902,7 @@ function toIssueStateType(value) {
|
|
|
66902
66902
|
case "canceled":
|
|
66903
66903
|
return value;
|
|
66904
66904
|
default:
|
|
66905
|
-
return "
|
|
66905
|
+
return "unknown";
|
|
66906
66906
|
}
|
|
66907
66907
|
}
|
|
66908
66908
|
function mapElicitationSignal(signal) {
|
|
@@ -67240,6 +67240,20 @@ ${truncatedStack}
|
|
|
67240
67240
|
const result = await Result.tryPromise({
|
|
67241
67241
|
try: async () => {
|
|
67242
67242
|
const issue = await this.client.issue(issueId);
|
|
67243
|
+
const state = await issue.state;
|
|
67244
|
+
if (!state) {
|
|
67245
|
+
throw new Error("Issue has no workflow state");
|
|
67246
|
+
}
|
|
67247
|
+
const stateType = toIssueStateType(state.type);
|
|
67248
|
+
if (stateType !== "unstarted") {
|
|
67249
|
+
this.log.info("Skipping issue auto-transition", {
|
|
67250
|
+
issueId,
|
|
67251
|
+
stateId: state.id,
|
|
67252
|
+
stateName: state.name,
|
|
67253
|
+
stateType
|
|
67254
|
+
});
|
|
67255
|
+
return;
|
|
67256
|
+
}
|
|
67243
67257
|
const team = await issue.team;
|
|
67244
67258
|
if (!team) {
|
|
67245
67259
|
throw new Error("Issue has no associated team");
|
|
@@ -67256,8 +67270,10 @@ ${truncatedStack}
|
|
|
67256
67270
|
this.log.warn("No In Progress state found", { issueId });
|
|
67257
67271
|
return;
|
|
67258
67272
|
}
|
|
67259
|
-
this.log.info("Moving issue to
|
|
67273
|
+
this.log.info("Moving issue to first started state", {
|
|
67260
67274
|
issueId,
|
|
67275
|
+
fromStateId: state.id,
|
|
67276
|
+
fromStateName: state.name,
|
|
67261
67277
|
stateId: inProgressState.id,
|
|
67262
67278
|
stateName: inProgressState.name
|
|
67263
67279
|
});
|
|
@@ -80973,7 +80989,7 @@ function generateSuccessHtml(result) {
|
|
|
80973
80989
|
|
|
80974
80990
|
<h2>Next Steps:</h2>
|
|
80975
80991
|
<ul>
|
|
80976
|
-
<li><strong>Optional:</strong> set <code>
|
|
80992
|
+
<li><strong>Optional:</strong> set <code>linearOrganizationId</code> in <code>~/.config/opencode-linear-agent/config.json</code> to restrict to one org:
|
|
80977
80993
|
<br><code>${result.organizationId}</code>
|
|
80978
80994
|
</li>
|
|
80979
80995
|
<li>Make sure your webhook URL is configured in Linear:
|
|
@@ -81322,88 +81338,91 @@ class OpencodeService {
|
|
|
81322
81338
|
}
|
|
81323
81339
|
// src/config.ts
|
|
81324
81340
|
import { homedir } from "os";
|
|
81325
|
-
import { resolve
|
|
81341
|
+
import path2, { resolve } from "path";
|
|
81342
|
+
|
|
81343
|
+
// ../../node_modules/.bun/xdg-basedir@5.1.0/node_modules/xdg-basedir/index.js
|
|
81344
|
+
import os from "os";
|
|
81345
|
+
import path from "path";
|
|
81346
|
+
var homeDirectory = os.homedir();
|
|
81347
|
+
var { env } = process;
|
|
81348
|
+
var xdgData = env.XDG_DATA_HOME || (homeDirectory ? path.join(homeDirectory, ".local", "share") : undefined);
|
|
81349
|
+
var xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ? path.join(homeDirectory, ".config") : undefined);
|
|
81350
|
+
var xdgState = env.XDG_STATE_HOME || (homeDirectory ? path.join(homeDirectory, ".local", "state") : undefined);
|
|
81351
|
+
var xdgCache = env.XDG_CACHE_HOME || (homeDirectory ? path.join(homeDirectory, ".cache") : undefined);
|
|
81352
|
+
var xdgRuntime = env.XDG_RUNTIME_DIR || undefined;
|
|
81353
|
+
var xdgDataDirectories = (env.XDG_DATA_DIRS || "/usr/local/share/:/usr/share/").split(":");
|
|
81354
|
+
if (xdgData) {
|
|
81355
|
+
xdgDataDirectories.unshift(xdgData);
|
|
81356
|
+
}
|
|
81357
|
+
var xdgConfigDirectories = (env.XDG_CONFIG_DIRS || "/etc/xdg").split(":");
|
|
81358
|
+
if (xdgConfig) {
|
|
81359
|
+
xdgConfigDirectories.unshift(xdgConfig);
|
|
81360
|
+
}
|
|
81361
|
+
|
|
81362
|
+
// src/config.ts
|
|
81363
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
81326
81364
|
var DEFAULT_WEBHOOK_IPS = [
|
|
81327
81365
|
"35.231.147.226",
|
|
81328
81366
|
"35.243.134.228",
|
|
81329
81367
|
"34.145.29.68"
|
|
81330
81368
|
];
|
|
81331
|
-
var
|
|
81332
|
-
|
|
81333
|
-
|
|
81334
|
-
|
|
81335
|
-
|
|
81336
|
-
|
|
81337
|
-
|
|
81338
|
-
|
|
81339
|
-
|
|
81340
|
-
|
|
81341
|
-
|
|
81342
|
-
|
|
81343
|
-
|
|
81344
|
-
|
|
81369
|
+
var ConfigFileSchema = exports_external.object({
|
|
81370
|
+
webhookServerPublicHostname: exports_external.string().min(1),
|
|
81371
|
+
webhookServerPort: exports_external.coerce.number().default(3210),
|
|
81372
|
+
opencodeServerUrl: exports_external.string().min(1).default("http://localhost:4096"),
|
|
81373
|
+
linearClientId: exports_external.string().min(1),
|
|
81374
|
+
linearClientSecret: exports_external.string().min(1),
|
|
81375
|
+
linearWebhookSecret: exports_external.string().min(1),
|
|
81376
|
+
linearWebhookIps: exports_external.array(exports_external.string()).min(1).default(DEFAULT_WEBHOOK_IPS),
|
|
81377
|
+
linearOrganizationId: exports_external.string().optional(),
|
|
81378
|
+
projectsPath: exports_external.string().min(1).transform((projectsPath) => {
|
|
81379
|
+
if (projectsPath.startsWith("~/")) {
|
|
81380
|
+
return resolve(homedir(), projectsPath.slice(2));
|
|
81381
|
+
}
|
|
81382
|
+
return projectsPath;
|
|
81383
|
+
})
|
|
81345
81384
|
});
|
|
81346
|
-
|
|
81347
|
-
|
|
81348
|
-
|
|
81385
|
+
var APPLICATION_DIRECTORY = "opencode-linear-agent";
|
|
81386
|
+
function loadConfig() {
|
|
81387
|
+
if (!xdgConfig) {
|
|
81388
|
+
throw new Error("Failed to find directory for config storage. Please ensure HOME or XDG_CONFIG_HOME environment variable is set.");
|
|
81349
81389
|
}
|
|
81350
|
-
|
|
81351
|
-
|
|
81352
|
-
|
|
81353
|
-
const value = process.env[name];
|
|
81354
|
-
if (!value) {
|
|
81355
|
-
throw new Error(`Missing required environment variable: ${name}`);
|
|
81390
|
+
const configPath = path2.join(xdgConfig, APPLICATION_DIRECTORY, "config.json");
|
|
81391
|
+
if (!existsSync3(configPath)) {
|
|
81392
|
+
throw new Error(`Config file not found at ${configPath}. Please create a config file with the necessary configuration values.`);
|
|
81356
81393
|
}
|
|
81357
|
-
|
|
81358
|
-
|
|
81359
|
-
|
|
81360
|
-
|
|
81361
|
-
|
|
81362
|
-
|
|
81363
|
-
|
|
81364
|
-
|
|
81365
|
-
|
|
81366
|
-
|
|
81367
|
-
opencode: {
|
|
81368
|
-
url: process.env["OPENCODE_URL"] ?? "http://localhost:4096"
|
|
81369
|
-
},
|
|
81370
|
-
linear: {
|
|
81371
|
-
clientId: requiredEnv("LINEAR_CLIENT_ID"),
|
|
81372
|
-
clientSecret: requiredEnv("LINEAR_CLIENT_SECRET"),
|
|
81373
|
-
webhookSecret: requiredEnv("LINEAR_WEBHOOK_SECRET"),
|
|
81374
|
-
organizationId: process.env["LINEAR_ORGANIZATION_ID"],
|
|
81375
|
-
webhookIps
|
|
81376
|
-
},
|
|
81377
|
-
projectsPath: requiredEnv("PROJECTS_PATH")
|
|
81378
|
-
};
|
|
81379
|
-
const result = ConfigSchema.safeParse(raw);
|
|
81394
|
+
const rawConfig = readFileSync(configPath, "utf-8");
|
|
81395
|
+
let raw;
|
|
81396
|
+
try {
|
|
81397
|
+
raw = JSON.parse(rawConfig);
|
|
81398
|
+
} catch (err2) {
|
|
81399
|
+
throw new Error(`Failed to parse config file at ${configPath}`, {
|
|
81400
|
+
cause: err2
|
|
81401
|
+
});
|
|
81402
|
+
}
|
|
81403
|
+
const result = ConfigFileSchema.safeParse(raw);
|
|
81380
81404
|
if (!result.success) {
|
|
81381
81405
|
const issues = result.error.issues.map((issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`).join(`
|
|
81382
81406
|
`);
|
|
81383
81407
|
throw new Error(`Invalid configuration:
|
|
81384
81408
|
${issues}`);
|
|
81385
81409
|
}
|
|
81386
|
-
return
|
|
81387
|
-
...result.data,
|
|
81388
|
-
projectsPath: expandPath(result.data.projectsPath)
|
|
81389
|
-
};
|
|
81390
|
-
}
|
|
81391
|
-
function getWorkerUrl(config2) {
|
|
81392
|
-
return `https://${config2.publicHostname}`;
|
|
81393
|
-
}
|
|
81394
|
-
function getDataDir() {
|
|
81395
|
-
return join2(homedir(), ".local/share/opencode-linear-agent");
|
|
81410
|
+
return result.data;
|
|
81396
81411
|
}
|
|
81397
81412
|
|
|
81398
81413
|
// src/storage/FileStore.ts
|
|
81399
|
-
import { mkdir } from "fs/promises";
|
|
81400
|
-
import { dirname } from "path";
|
|
81414
|
+
import { mkdir, writeFile, readFile, exists } from "fs/promises";
|
|
81415
|
+
import { dirname, join as join2 } from "path";
|
|
81401
81416
|
class FileStore {
|
|
81402
|
-
filePath;
|
|
81403
81417
|
data = {};
|
|
81404
81418
|
loaded = false;
|
|
81405
|
-
|
|
81406
|
-
|
|
81419
|
+
filePath;
|
|
81420
|
+
constructor() {
|
|
81421
|
+
if (!xdgData) {
|
|
81422
|
+
throw new Error("Failed to find directory for data storage. Please ensure HOME or XDG_DATA_HOME environment variable is set.");
|
|
81423
|
+
}
|
|
81424
|
+
const dataDir = join2(xdgData, APPLICATION_DIRECTORY, "store.json");
|
|
81425
|
+
this.filePath = dataDir;
|
|
81407
81426
|
}
|
|
81408
81427
|
async ensureLoaded() {
|
|
81409
81428
|
if (this.loaded) {
|
|
@@ -81412,20 +81431,23 @@ class FileStore {
|
|
|
81412
81431
|
await this.reload();
|
|
81413
81432
|
}
|
|
81414
81433
|
async reload() {
|
|
81415
|
-
|
|
81416
|
-
|
|
81417
|
-
|
|
81418
|
-
|
|
81419
|
-
|
|
81420
|
-
|
|
81421
|
-
|
|
81422
|
-
|
|
81434
|
+
if (!await exists(this.filePath)) {
|
|
81435
|
+
this.data = {};
|
|
81436
|
+
this.loaded = true;
|
|
81437
|
+
return;
|
|
81438
|
+
}
|
|
81439
|
+
const file2 = await readFile(this.filePath);
|
|
81440
|
+
try {
|
|
81441
|
+
const json2 = JSON.parse(file2.toString());
|
|
81442
|
+
this.data = parseStoreData(json2);
|
|
81443
|
+
} catch {
|
|
81444
|
+
this.data = {};
|
|
81423
81445
|
}
|
|
81424
81446
|
this.loaded = true;
|
|
81425
81447
|
}
|
|
81426
81448
|
async save() {
|
|
81427
81449
|
await mkdir(dirname(this.filePath), { recursive: true });
|
|
81428
|
-
await
|
|
81450
|
+
await writeFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
81429
81451
|
}
|
|
81430
81452
|
isExpired(stored) {
|
|
81431
81453
|
if (!stored.expires) {
|
|
@@ -81925,7 +81947,6 @@ async function dispatchAgentSessionEvent(event, linear2, opencode2, sessionRepos
|
|
|
81925
81947
|
}
|
|
81926
81948
|
|
|
81927
81949
|
// src/index.ts
|
|
81928
|
-
import { join as join4 } from "path";
|
|
81929
81950
|
function getClientIp(request) {
|
|
81930
81951
|
const cfIp = request.headers.get("cf-connecting-ip");
|
|
81931
81952
|
if (cfIp) {
|
|
@@ -81946,7 +81967,7 @@ function isAllowedIp(ip, allowlist) {
|
|
|
81946
81967
|
}
|
|
81947
81968
|
function createDirectDispatcher(config2, tokenStore, sessionRepository) {
|
|
81948
81969
|
const opencodeClient = createOpencodeClient({
|
|
81949
|
-
baseUrl: config2.
|
|
81970
|
+
baseUrl: config2.opencodeServerUrl
|
|
81950
81971
|
});
|
|
81951
81972
|
const opencode2 = new OpencodeService(opencodeClient);
|
|
81952
81973
|
return {
|
|
@@ -81956,8 +81977,8 @@ function createDirectDispatcher(config2, tokenStore, sessionRepository) {
|
|
|
81956
81977
|
let accessToken2 = await tokenStore.getAccessToken(organizationId);
|
|
81957
81978
|
if (!accessToken2) {
|
|
81958
81979
|
const oauthConfig = {
|
|
81959
|
-
clientId: config2.
|
|
81960
|
-
clientSecret: config2.
|
|
81980
|
+
clientId: config2.linearClientId,
|
|
81981
|
+
clientSecret: config2.linearClientSecret
|
|
81961
81982
|
};
|
|
81962
81983
|
accessToken2 = await refreshAccessToken(oauthConfig, tokenStore, organizationId);
|
|
81963
81984
|
}
|
|
@@ -81971,8 +81992,8 @@ function createDirectDispatcher(config2, tokenStore, sessionRepository) {
|
|
|
81971
81992
|
if (!accessToken) {
|
|
81972
81993
|
Log.create({ service: "dispatcher" }).tag("organizationId", organizationId).info("No access token, attempting refresh");
|
|
81973
81994
|
const oauthConfig = {
|
|
81974
|
-
clientId: config2.
|
|
81975
|
-
clientSecret: config2.
|
|
81995
|
+
clientId: config2.linearClientId,
|
|
81996
|
+
clientSecret: config2.linearClientSecret
|
|
81976
81997
|
};
|
|
81977
81998
|
accessToken = await refreshAccessToken(oauthConfig, tokenStore, organizationId);
|
|
81978
81999
|
}
|
|
@@ -81986,12 +82007,12 @@ function createDirectDispatcher(config2, tokenStore, sessionRepository) {
|
|
|
81986
82007
|
}
|
|
81987
82008
|
function createServer(config2, kv, tokenStore, dispatcher) {
|
|
81988
82009
|
const oauthConfig = {
|
|
81989
|
-
clientId: config2.
|
|
81990
|
-
clientSecret: config2.
|
|
81991
|
-
baseUrl:
|
|
82010
|
+
clientId: config2.linearClientId,
|
|
82011
|
+
clientSecret: config2.linearClientSecret,
|
|
82012
|
+
baseUrl: `https://${config2.webhookServerPublicHostname}`
|
|
81992
82013
|
};
|
|
81993
82014
|
return Bun.serve({
|
|
81994
|
-
port: config2.
|
|
82015
|
+
port: config2.webhookServerPort,
|
|
81995
82016
|
async fetch(request) {
|
|
81996
82017
|
const url2 = new URL(request.url);
|
|
81997
82018
|
const pathname = url2.pathname;
|
|
@@ -82006,24 +82027,24 @@ function createServer(config2, kv, tokenStore, dispatcher) {
|
|
|
82006
82027
|
});
|
|
82007
82028
|
return response;
|
|
82008
82029
|
};
|
|
82009
|
-
if (pathname === "/api/oauth/authorize") {
|
|
82010
|
-
return respond(await handleAuthorize(request, oauthConfig, kv));
|
|
82011
|
-
}
|
|
82012
82030
|
if (pathname === "/health") {
|
|
82013
82031
|
return respond(new Response("OK", { status: 200 }));
|
|
82014
82032
|
}
|
|
82033
|
+
if (pathname === "/api/oauth/authorize") {
|
|
82034
|
+
return respond(await handleAuthorize(request, oauthConfig, kv));
|
|
82035
|
+
}
|
|
82015
82036
|
if (pathname === "/api/oauth/callback") {
|
|
82016
82037
|
return respond(await handleCallback(request, oauthConfig, kv, tokenStore));
|
|
82017
82038
|
}
|
|
82018
|
-
if (pathname === "/api/webhook/linear"
|
|
82019
|
-
if (!isAllowedIp(clientIp, config2.
|
|
82039
|
+
if (pathname === "/api/webhook/linear") {
|
|
82040
|
+
if (!isAllowedIp(clientIp, config2.linearWebhookIps)) {
|
|
82020
82041
|
log.warn("Webhook request from unauthorized IP", {
|
|
82021
82042
|
clientIp,
|
|
82022
|
-
allowedIps: config2.
|
|
82043
|
+
allowedIps: config2.linearWebhookIps
|
|
82023
82044
|
});
|
|
82024
82045
|
return respond(new Response("Forbidden", { status: 403 }));
|
|
82025
82046
|
}
|
|
82026
|
-
return respond(await handleWebhook(request, config2.
|
|
82047
|
+
return respond(await handleWebhook(request, config2.linearWebhookSecret, tokenStore, dispatcher, undefined, config2.linearOrganizationId));
|
|
82027
82048
|
}
|
|
82028
82049
|
return respond(new Response("Not found", { status: 404 }));
|
|
82029
82050
|
}
|
|
@@ -82031,14 +82052,14 @@ function createServer(config2, kv, tokenStore, dispatcher) {
|
|
|
82031
82052
|
}
|
|
82032
82053
|
function startTokenRefreshTimer(config2, tokenStore) {
|
|
82033
82054
|
const log = Log.create({ service: "token-refresh" });
|
|
82034
|
-
const organizationId = config2.
|
|
82055
|
+
const organizationId = config2.linearOrganizationId;
|
|
82035
82056
|
if (!organizationId) {
|
|
82036
82057
|
log.info("Skipping proactive token refresh (LINEAR_ORGANIZATION_ID not set)");
|
|
82037
82058
|
return;
|
|
82038
82059
|
}
|
|
82039
82060
|
const oauthConfig = {
|
|
82040
|
-
clientId: config2.
|
|
82041
|
-
clientSecret: config2.
|
|
82061
|
+
clientId: config2.linearClientId,
|
|
82062
|
+
clientSecret: config2.linearClientSecret
|
|
82042
82063
|
};
|
|
82043
82064
|
const REFRESH_INTERVAL_MS = 20 * 60 * 60 * 1000;
|
|
82044
82065
|
const refresh = async () => {
|
|
@@ -82054,61 +82075,37 @@ function startTokenRefreshTimer(config2, tokenStore) {
|
|
|
82054
82075
|
refresh();
|
|
82055
82076
|
setInterval(() => void refresh(), REFRESH_INTERVAL_MS);
|
|
82056
82077
|
}
|
|
82057
|
-
async function validateStoreFile(filePath, log) {
|
|
82058
|
-
const file2 = Bun.file(filePath);
|
|
82059
|
-
if (!await file2.exists()) {
|
|
82060
|
-
return;
|
|
82061
|
-
}
|
|
82062
|
-
const result = await Result.tryPromise({
|
|
82063
|
-
try: async () => {
|
|
82064
|
-
const json2 = await file2.json();
|
|
82065
|
-
parseStoreData(json2);
|
|
82066
|
-
},
|
|
82067
|
-
catch: (e) => e instanceof Error ? e.message : String(e)
|
|
82068
|
-
});
|
|
82069
|
-
if (Result.isError(result)) {
|
|
82070
|
-
log.warn("Invalid shared store file detected", {
|
|
82071
|
-
dataPath: filePath,
|
|
82072
|
-
error: result.error,
|
|
82073
|
-
recovery: "Fix/restore store.json, restart server, then re-auth Linear if token data was lost."
|
|
82074
|
-
});
|
|
82075
|
-
}
|
|
82076
|
-
}
|
|
82077
82078
|
async function main() {
|
|
82078
82079
|
const log = Log.create({ service: "startup" });
|
|
82079
82080
|
log.info("Starting Linear OpenCode Agent (Local)");
|
|
82080
82081
|
const config2 = loadConfig();
|
|
82081
82082
|
log.info("Configuration loaded", {
|
|
82082
|
-
port: config2.
|
|
82083
|
-
publicHostname: config2.
|
|
82084
|
-
opencodeUrl: config2.
|
|
82083
|
+
port: config2.webhookServerPort,
|
|
82084
|
+
publicHostname: config2.webhookServerPublicHostname,
|
|
82085
|
+
opencodeUrl: config2.opencodeServerUrl,
|
|
82085
82086
|
projectsPath: config2.projectsPath
|
|
82086
82087
|
});
|
|
82087
|
-
const
|
|
82088
|
-
const dataPath = join4(dataDir, "store.json");
|
|
82089
|
-
const kv = new FileStore(dataPath);
|
|
82088
|
+
const kv = new FileStore;
|
|
82090
82089
|
const tokenStore = new FileTokenStore(kv);
|
|
82091
82090
|
const sessionRepository = new FileSessionRepository(kv);
|
|
82092
|
-
log.info("Storage initialized", { dataPath });
|
|
82093
|
-
await validateStoreFile(dataPath, log);
|
|
82094
82091
|
startTokenRefreshTimer(config2, tokenStore);
|
|
82095
82092
|
const dispatcher = createDirectDispatcher(config2, tokenStore, sessionRepository);
|
|
82096
82093
|
const server2 = createServer(config2, kv, tokenStore, dispatcher);
|
|
82097
|
-
const
|
|
82094
|
+
const webhookServerUrl = `https://${config2.webhookServerPublicHostname}`;
|
|
82098
82095
|
log.info("Server started", {
|
|
82099
|
-
port: config2.
|
|
82100
|
-
|
|
82101
|
-
webhookUrl: `${
|
|
82102
|
-
oauthUrl: `${
|
|
82096
|
+
port: config2.webhookServerPort,
|
|
82097
|
+
webhookServerUrl,
|
|
82098
|
+
webhookUrl: `${webhookServerUrl}/api/webhook/linear`,
|
|
82099
|
+
oauthUrl: `${webhookServerUrl}/api/oauth/authorize`
|
|
82103
82100
|
});
|
|
82104
82101
|
process.stdout.write(`
|
|
82105
82102
|
Linear OpenCode Agent (Local) running!
|
|
82106
82103
|
|
|
82107
|
-
Local: http://localhost:${config2.
|
|
82108
|
-
Public: ${
|
|
82104
|
+
Local: http://localhost:${config2.webhookServerPort}
|
|
82105
|
+
Public: ${webhookServerUrl}
|
|
82109
82106
|
|
|
82110
|
-
Webhook URL: ${
|
|
82111
|
-
OAuth URL: ${
|
|
82107
|
+
Webhook URL: ${webhookServerUrl}/api/webhook/linear
|
|
82108
|
+
OAuth URL: ${webhookServerUrl}/api/oauth/authorize
|
|
82112
82109
|
|
|
82113
82110
|
Make sure OpenCode is running: opencode serve
|
|
82114
82111
|
`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencode-linear-agent/server",
|
|
3
|
-
"version": "0.1.3-master.
|
|
3
|
+
"version": "0.1.3-master.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@linear/sdk": "catalog:",
|
|
34
34
|
"@opencode-ai/sdk": "catalog:",
|
|
35
35
|
"better-result": "catalog:",
|
|
36
|
+
"xdg-basedir": "catalog:",
|
|
36
37
|
"zod": "catalog:"
|
|
37
38
|
},
|
|
38
39
|
"peerDependencies": {
|