@shipers-dev/dayline 0.95.0 → 0.97.0
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 +204 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -45260,7 +45260,7 @@ import { parseArgs } from "util";
|
|
|
45260
45260
|
// package.json
|
|
45261
45261
|
var package_default = {
|
|
45262
45262
|
name: "@shipers-dev/dayline",
|
|
45263
|
-
version: "0.
|
|
45263
|
+
version: "0.97.0",
|
|
45264
45264
|
type: "module",
|
|
45265
45265
|
bin: {
|
|
45266
45266
|
dayline: "./dist/index.js"
|
|
@@ -46573,6 +46573,7 @@ function buildProjectTree(args2) {
|
|
|
46573
46573
|
}
|
|
46574
46574
|
|
|
46575
46575
|
// src/_impl/daemon-main.ts
|
|
46576
|
+
init_paths();
|
|
46576
46577
|
init_errors();
|
|
46577
46578
|
|
|
46578
46579
|
// src/_impl/funnel.ts
|
|
@@ -46673,12 +46674,116 @@ function parseChatMessageBody(raw) {
|
|
|
46673
46674
|
return { ok: true, value: { chat_id: chatId, text } };
|
|
46674
46675
|
}
|
|
46675
46676
|
|
|
46677
|
+
// src/_impl/api-proxy.ts
|
|
46678
|
+
//! The daemon's forwarder to the worker API.
|
|
46679
|
+
//!
|
|
46680
|
+
//! Native clients (the desktop app) never reach the worker themselves: every
|
|
46681
|
+
//! call they make rides `/api/**` on the local daemon, so the only host the
|
|
46682
|
+
//! app connects to is 127.0.0.1 and exactly one place — here — decides what
|
|
46683
|
+
//! leaves the machine. Split out of daemon-main so the allowlist is readable
|
|
46684
|
+
//! on its own and testable without standing up a server.
|
|
46685
|
+
var API_PROXY_ROUTES = [
|
|
46686
|
+
{ method: "GET", path: "/api/auth/me" },
|
|
46687
|
+
{ method: "GET", path: "/api/workspaces/:ws/agents" },
|
|
46688
|
+
{ method: "GET", path: "/api/workspaces/:ws/devices" },
|
|
46689
|
+
{ method: "GET", path: "/api/workspaces/:ws/skills" },
|
|
46690
|
+
{ method: "GET", path: "/api/workspaces/:ws/projects" },
|
|
46691
|
+
{ method: "GET", path: "/api/workspaces/:ws/projects/:project/chats" },
|
|
46692
|
+
{ method: "POST", path: "/api/workspaces/:ws/projects/:project/chats" },
|
|
46693
|
+
{ method: "GET", path: "/api/workspaces/:ws/chats/:chat" },
|
|
46694
|
+
{ method: "PATCH", path: "/api/workspaces/:ws/chats/:chat" },
|
|
46695
|
+
{ method: "DELETE", path: "/api/workspaces/:ws/chats/:chat" },
|
|
46696
|
+
{ method: "GET", path: "/api/workspaces/:ws/chats/:chat/snapshot" },
|
|
46697
|
+
{ method: "POST", path: "/api/workspaces/:ws/agent/chats/query" },
|
|
46698
|
+
{ method: "GET", path: "/api/workspaces/:ws/projects/:project/issues" },
|
|
46699
|
+
{ method: "POST", path: "/api/workspaces/:ws/projects/:project/issues" },
|
|
46700
|
+
{ method: "GET", path: "/api/workspaces/:ws/projects/:project/issues/:issue" },
|
|
46701
|
+
{ method: "PATCH", path: "/api/workspaces/:ws/projects/:project/issues/:issue" },
|
|
46702
|
+
{ method: "DELETE", path: "/api/workspaces/:ws/projects/:project/issues/:issue" },
|
|
46703
|
+
{ method: "POST", path: "/api/workspaces/:ws/projects/:project/issues/:issue/complete" },
|
|
46704
|
+
{ method: "POST", path: "/api/workspaces/:ws/projects/:project/issues/:issue/fail" },
|
|
46705
|
+
{ method: "POST", path: "/api/workspaces/:ws/projects/:project/issues/:issue/stop" },
|
|
46706
|
+
{ method: "GET", path: "/api/workspaces/:ws/projects/:project/issues/:issue/dispatches" },
|
|
46707
|
+
{ method: "GET", path: "/api/workspaces/:ws/projects/:project/issues/:issue/activity" },
|
|
46708
|
+
{ method: "GET", path: "/api/workspaces/:ws/projects/:project/devices" },
|
|
46709
|
+
{ method: "PATCH", path: "/api/workspaces/:ws/projects/:project/devices/:device" },
|
|
46710
|
+
{ method: "POST", path: "/api/workspaces/:ws/projects/:project/devices/:device/prepare-working-dir" },
|
|
46711
|
+
{ method: "POST", path: "/api/workspaces/:ws/projects/:project/devices/:device/worktree" },
|
|
46712
|
+
{ method: "GET", path: "/api/devices/:device/fs/*" }
|
|
46713
|
+
];
|
|
46714
|
+
var API_PROXY_MATCHERS = API_PROXY_ROUTES.map(({ method, path }) => ({
|
|
46715
|
+
method,
|
|
46716
|
+
re: new RegExp(`^${path.split("/").map((seg) => seg.startsWith(":") ? "[^/]+" : seg === "*" ? ".+" : seg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("/")}$`)
|
|
46717
|
+
}));
|
|
46718
|
+
function apiProxyAllows(method, pathname) {
|
|
46719
|
+
return API_PROXY_MATCHERS.some((r) => r.method === method && r.re.test(pathname));
|
|
46720
|
+
}
|
|
46721
|
+
var HOP_BY_HOP = new Set([
|
|
46722
|
+
"host",
|
|
46723
|
+
"connection",
|
|
46724
|
+
"keep-alive",
|
|
46725
|
+
"proxy-authenticate",
|
|
46726
|
+
"proxy-authorization",
|
|
46727
|
+
"te",
|
|
46728
|
+
"trailer",
|
|
46729
|
+
"transfer-encoding",
|
|
46730
|
+
"upgrade",
|
|
46731
|
+
"content-length",
|
|
46732
|
+
"authorization",
|
|
46733
|
+
"origin",
|
|
46734
|
+
"referer",
|
|
46735
|
+
"accept-encoding"
|
|
46736
|
+
]);
|
|
46737
|
+
function makeApiProxy(apiUrl, session) {
|
|
46738
|
+
return async function proxyToApi(req, url2) {
|
|
46739
|
+
const userSession = session();
|
|
46740
|
+
if (!userSession) {
|
|
46741
|
+
return Response.json({
|
|
46742
|
+
ok: false,
|
|
46743
|
+
code: "no_device_session",
|
|
46744
|
+
message: "this device has no user session — re-pair it with `dayline connect`"
|
|
46745
|
+
}, { status: 401 });
|
|
46746
|
+
}
|
|
46747
|
+
const target = new URL(`${apiUrl.replace(/\/+$/, "")}${url2.pathname}`);
|
|
46748
|
+
target.search = url2.search;
|
|
46749
|
+
target.searchParams.delete("token");
|
|
46750
|
+
const headers = new Headers;
|
|
46751
|
+
for (const [k, v] of req.headers) {
|
|
46752
|
+
if (!HOP_BY_HOP.has(k.toLowerCase()))
|
|
46753
|
+
headers.set(k, v);
|
|
46754
|
+
}
|
|
46755
|
+
headers.set("authorization", `Bearer ${userSession}`);
|
|
46756
|
+
const method = req.method.toUpperCase();
|
|
46757
|
+
const body = method === "GET" || method === "HEAD" ? undefined : await req.arrayBuffer();
|
|
46758
|
+
let upstream;
|
|
46759
|
+
try {
|
|
46760
|
+
upstream = await fetch(target.toString(), {
|
|
46761
|
+
method,
|
|
46762
|
+
headers,
|
|
46763
|
+
body,
|
|
46764
|
+
redirect: "manual",
|
|
46765
|
+
signal: AbortSignal.timeout(60000)
|
|
46766
|
+
});
|
|
46767
|
+
} catch (e) {
|
|
46768
|
+
return Response.json({ ok: false, code: "upstream_unreachable", message: String(e?.message || e) }, { status: 502 });
|
|
46769
|
+
}
|
|
46770
|
+
const bytes = await upstream.arrayBuffer();
|
|
46771
|
+
const out = new Headers;
|
|
46772
|
+
for (const name of ["content-type", "cache-control", "etag", "location"]) {
|
|
46773
|
+
const v = upstream.headers.get(name);
|
|
46774
|
+
if (v)
|
|
46775
|
+
out.set(name, v);
|
|
46776
|
+
}
|
|
46777
|
+
return new Response(bytes, { status: upstream.status, headers: out });
|
|
46778
|
+
};
|
|
46779
|
+
}
|
|
46780
|
+
|
|
46676
46781
|
// src/_impl/daemon-main.ts
|
|
46677
46782
|
init_lib();
|
|
46678
46783
|
// package.json
|
|
46679
46784
|
var package_default2 = {
|
|
46680
46785
|
name: "@shipers-dev/dayline",
|
|
46681
|
-
version: "0.
|
|
46786
|
+
version: "0.97.0",
|
|
46682
46787
|
type: "module",
|
|
46683
46788
|
bin: {
|
|
46684
46789
|
dayline: "./dist/index.js"
|
|
@@ -46943,7 +47048,7 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
|
|
|
46943
47048
|
let projectTreeCache = null;
|
|
46944
47049
|
const corsHeaders = (origin) => ({
|
|
46945
47050
|
"Access-Control-Allow-Origin": origin || "*",
|
|
46946
|
-
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
47051
|
+
"Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
|
|
46947
47052
|
"Access-Control-Allow-Headers": "Authorization, Content-Type",
|
|
46948
47053
|
"Access-Control-Max-Age": "600",
|
|
46949
47054
|
Vary: "Origin"
|
|
@@ -46959,6 +47064,35 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
|
|
|
46959
47064
|
}
|
|
46960
47065
|
return null;
|
|
46961
47066
|
};
|
|
47067
|
+
let sessionToken = cfg.sessionToken ?? null;
|
|
47068
|
+
const proxyToApi = makeApiProxy(apiUrl, () => sessionToken);
|
|
47069
|
+
const sessionRoute = async (req) => {
|
|
47070
|
+
if (req.method !== "GET" && req.method !== "DELETE") {
|
|
47071
|
+
return Response.json({ error: "method not allowed" }, { status: 405 });
|
|
47072
|
+
}
|
|
47073
|
+
if (req.method === "DELETE") {
|
|
47074
|
+
sessionToken = null;
|
|
47075
|
+
try {
|
|
47076
|
+
const current = JSON.parse(await Bun.file(CONFIG_PATH).text());
|
|
47077
|
+
delete current.sessionToken;
|
|
47078
|
+
await Bun.write(CONFIG_PATH, JSON.stringify(current, null, 2));
|
|
47079
|
+
} catch (e) {
|
|
47080
|
+
log3(`session: could not persist sign-out: ${e}`);
|
|
47081
|
+
}
|
|
47082
|
+
return Response.json({ signed_in: false });
|
|
47083
|
+
}
|
|
47084
|
+
if (!sessionToken) {
|
|
47085
|
+
return Response.json({ signed_in: false, reason: "no_device_session" });
|
|
47086
|
+
}
|
|
47087
|
+
const me = await proxyToApi(new Request(`http://127.0.0.1/api/auth/me`, { headers: req.headers }), new URL("http://127.0.0.1/api/auth/me"));
|
|
47088
|
+
if (me.status === 401) {
|
|
47089
|
+
return Response.json({ signed_in: false, reason: "session_expired" });
|
|
47090
|
+
}
|
|
47091
|
+
if (!me.ok) {
|
|
47092
|
+
return Response.json({ signed_in: false, reason: "api_unreachable" }, { status: 502 });
|
|
47093
|
+
}
|
|
47094
|
+
return Response.json({ signed_in: true, me: await me.json() });
|
|
47095
|
+
};
|
|
46962
47096
|
const withCors = (res, origin) => {
|
|
46963
47097
|
for (const [k, v] of Object.entries(corsHeaders(origin))) {
|
|
46964
47098
|
res.headers.set(k, v);
|
|
@@ -47005,6 +47139,21 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
|
|
|
47005
47139
|
const route = () => {
|
|
47006
47140
|
if (url2.pathname === "/health")
|
|
47007
47141
|
return Response.json({ ok: true, device_id: cfg.deviceId, cli_version: CLI_VERSION });
|
|
47142
|
+
if (url2.pathname === "/session") {
|
|
47143
|
+
const denied = requireLocalAuth(req, url2, { allowQueryToken: true });
|
|
47144
|
+
if (denied)
|
|
47145
|
+
return denied;
|
|
47146
|
+
return sessionRoute(req);
|
|
47147
|
+
}
|
|
47148
|
+
if (url2.pathname === "/api" || url2.pathname.startsWith("/api/")) {
|
|
47149
|
+
const denied = requireLocalAuth(req, url2, { allowQueryToken: true });
|
|
47150
|
+
if (denied)
|
|
47151
|
+
return denied;
|
|
47152
|
+
if (!apiProxyAllows(req.method.toUpperCase(), url2.pathname)) {
|
|
47153
|
+
return Response.json({ ok: false, code: "route_not_proxied", message: `${req.method} ${url2.pathname} is not a proxied API route` }, { status: 404 });
|
|
47154
|
+
}
|
|
47155
|
+
return proxyToApi(req, url2);
|
|
47156
|
+
}
|
|
47008
47157
|
if (url2.pathname === "/files" && req.method === "GET") {
|
|
47009
47158
|
if (req.headers.get("authorization") !== expectedAuth)
|
|
47010
47159
|
return new Response("unauthorized", { status: 401 });
|
|
@@ -48135,7 +48284,8 @@ async function awaitPairing(apiUrl, deviceName, opts = {}) {
|
|
|
48135
48284
|
device_id: j.device_id,
|
|
48136
48285
|
token: j.token,
|
|
48137
48286
|
dispatch_secret: j.dispatch_secret ?? null,
|
|
48138
|
-
workspace_id: j.workspace_id ?? null
|
|
48287
|
+
workspace_id: j.workspace_id ?? null,
|
|
48288
|
+
session: j.session ?? null
|
|
48139
48289
|
};
|
|
48140
48290
|
}
|
|
48141
48291
|
}
|
|
@@ -48154,7 +48304,7 @@ var connectCmd = exports_Effect.fn("connectCmd")(function* () {
|
|
|
48154
48304
|
const api2 = yield* Api2;
|
|
48155
48305
|
yield* config2.ensureDirs;
|
|
48156
48306
|
let cfg = yield* config2.load;
|
|
48157
|
-
if (!cfg.deviceId || !cfg.authToken) {
|
|
48307
|
+
if (!cfg.deviceId || !cfg.authToken || !cfg.sessionToken) {
|
|
48158
48308
|
const apiUrl = cfg.apiUrl || process.env.MULTI_API || process.env.MULTI_API_URL || "https://api.dayline.ai";
|
|
48159
48309
|
const name = deviceNameFromEnv();
|
|
48160
48310
|
const detected = yield* exports_Effect.promise(() => detectAgents());
|
|
@@ -48171,10 +48321,14 @@ var connectCmd = exports_Effect.fn("connectCmd")(function* () {
|
|
|
48171
48321
|
deviceId: bundle.device_id,
|
|
48172
48322
|
authToken: bundle.token,
|
|
48173
48323
|
dispatchSecret: bundle.dispatch_secret ?? undefined,
|
|
48174
|
-
workspaceId: bundle.workspace_id ?? undefined
|
|
48324
|
+
workspaceId: bundle.workspace_id ?? undefined,
|
|
48325
|
+
sessionToken: bundle.session ?? undefined
|
|
48175
48326
|
});
|
|
48176
48327
|
cfg = yield* config2.load;
|
|
48177
48328
|
yield* logger.log(`connect: paired device=${cfg.deviceId} workspace=${cfg.workspaceId}`);
|
|
48329
|
+
if (!cfg.sessionToken) {
|
|
48330
|
+
yield* logger.log("connect: this API did not issue a device session — desktop clients will stay signed out");
|
|
48331
|
+
}
|
|
48178
48332
|
}
|
|
48179
48333
|
if (!cfg.dispatchSecret) {
|
|
48180
48334
|
return yield* exports_Effect.fail(new UsageError({ message: "Missing dispatch secret. Re-pair via 'dayline setup'." }));
|
|
@@ -48214,7 +48368,7 @@ ${err?.stack ?? ""}`);
|
|
|
48214
48368
|
// src/commands/service.ts
|
|
48215
48369
|
init_esm();
|
|
48216
48370
|
import { existsSync as existsSync18, readFileSync as readFileSync13 } from "fs";
|
|
48217
|
-
import { homedir as homedir6, platform } from "os";
|
|
48371
|
+
import { homedir as homedir6, platform, tmpdir } from "os";
|
|
48218
48372
|
import { join as join19 } from "path";
|
|
48219
48373
|
init_errors();
|
|
48220
48374
|
init_paths();
|
|
@@ -48242,15 +48396,54 @@ var readPid = () => {
|
|
|
48242
48396
|
return null;
|
|
48243
48397
|
}
|
|
48244
48398
|
};
|
|
48399
|
+
function isEphemeralBinary(path) {
|
|
48400
|
+
if (!path)
|
|
48401
|
+
return true;
|
|
48402
|
+
return path.includes("/bunx-") || path.includes("/_npx/") || path.startsWith(tmpdir() + "/") || /\/T\/bunx/.test(path);
|
|
48403
|
+
}
|
|
48404
|
+
function pickStableBinary(candidates, exists4) {
|
|
48405
|
+
for (const c of candidates) {
|
|
48406
|
+
if (!c)
|
|
48407
|
+
continue;
|
|
48408
|
+
if (isEphemeralBinary(c))
|
|
48409
|
+
continue;
|
|
48410
|
+
if (exists4(c))
|
|
48411
|
+
return c;
|
|
48412
|
+
}
|
|
48413
|
+
return null;
|
|
48414
|
+
}
|
|
48415
|
+
function stableCandidates(fromWhich) {
|
|
48416
|
+
const home = homedir6();
|
|
48417
|
+
return [
|
|
48418
|
+
fromWhich,
|
|
48419
|
+
join19(home, ".bun/bin/dayline"),
|
|
48420
|
+
"/opt/homebrew/bin/dayline",
|
|
48421
|
+
"/usr/local/bin/dayline",
|
|
48422
|
+
join19(home, ".npm-global/bin/dayline")
|
|
48423
|
+
];
|
|
48424
|
+
}
|
|
48245
48425
|
var resolveBinary = exports_Effect.fn("service.resolveBinary")(function* () {
|
|
48246
48426
|
const proc = Bun.spawn(["which", "dayline"], { stdout: "pipe", stderr: "pipe" });
|
|
48247
48427
|
yield* exports_Effect.promise(() => proc.exited);
|
|
48248
48428
|
const out = yield* exports_Effect.promise(() => new Response(proc.stdout).text());
|
|
48249
|
-
const
|
|
48250
|
-
|
|
48251
|
-
|
|
48429
|
+
const fromWhich = out.trim();
|
|
48430
|
+
const stable = pickStableBinary(stableCandidates(fromWhich), existsSync18);
|
|
48431
|
+
if (stable)
|
|
48432
|
+
return stable;
|
|
48433
|
+
console.log("Installing @shipers-dev/dayline globally so the service has a stable binary…");
|
|
48434
|
+
const bun = Bun.which("bun");
|
|
48435
|
+
if (bun) {
|
|
48436
|
+
const add6 = Bun.spawn([bun, "add", "-g", "@shipers-dev/dayline@latest"], {
|
|
48437
|
+
stdout: "inherit",
|
|
48438
|
+
stderr: "inherit"
|
|
48439
|
+
});
|
|
48440
|
+
yield* exports_Effect.promise(() => add6.exited);
|
|
48441
|
+
}
|
|
48442
|
+
const afterInstall = pickStableBinary(stableCandidates(""), existsSync18);
|
|
48443
|
+
if (afterInstall)
|
|
48444
|
+
return afterInstall;
|
|
48252
48445
|
return yield* exports_Effect.fail(new DaemonError({
|
|
48253
|
-
message: "Could not find `dayline
|
|
48446
|
+
message: "Could not find an installed `dayline`. Install it with `bun add -g @shipers-dev/dayline`, then re-run `dayline service install`."
|
|
48254
48447
|
}));
|
|
48255
48448
|
});
|
|
48256
48449
|
var run6 = exports_Effect.fn("service.run")(function* (cmd, args2) {
|