skydive-cli 0.2.0 → 0.3.0-beta.773
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/CHANGELOG.md +42 -0
- package/README.md +114 -22
- package/dist/js/bin.mjs +1261 -128
- package/dist/js/{boot-CGaXUEer.mjs → boot-BOgO_O-u.mjs} +2573 -199
- package/dist/js/chat/tui/chunks/grammars/assets/bash.highlights.scm +56 -0
- package/dist/js/chat/tui/chunks/grammars/assets/bash.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/c.highlights.scm +81 -0
- package/dist/js/chat/tui/chunks/grammars/assets/c.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/cpp.highlights.scm +70 -0
- package/dist/js/chat/tui/chunks/grammars/assets/cpp.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/css.highlights.scm +76 -0
- package/dist/js/chat/tui/chunks/grammars/assets/css.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/go.highlights.scm +123 -0
- package/dist/js/chat/tui/chunks/grammars/assets/go.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/html.highlights.scm +13 -0
- package/dist/js/chat/tui/chunks/grammars/assets/html.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/java.highlights.scm +149 -0
- package/dist/js/chat/tui/chunks/grammars/assets/java.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/json.highlights.scm +16 -0
- package/dist/js/chat/tui/chunks/grammars/assets/json.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/lua.highlights.scm +201 -0
- package/dist/js/chat/tui/chunks/grammars/assets/lua.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/python.highlights.scm +137 -0
- package/dist/js/chat/tui/chunks/grammars/assets/python.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/ruby.highlights.scm +154 -0
- package/dist/js/chat/tui/chunks/grammars/assets/ruby.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/rust.highlights.scm +161 -0
- package/dist/js/chat/tui/chunks/grammars/assets/rust.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/toml.highlights.scm +53 -0
- package/dist/js/chat/tui/chunks/grammars/assets/toml.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/assets/yaml.highlights.scm +79 -0
- package/dist/js/chat/tui/chunks/grammars/assets/yaml.wasm +0 -0
- package/dist/js/chat/tui/chunks/grammars/grammars.ts +146 -0
- package/dist/js/{client-B3ZhhF7e.mjs → client-Dc7GZ3PG.mjs} +148 -8
- package/dist/js/client-DuwxEDG4.mjs +5 -0
- package/dist/js/daemon-Ch_MSDmQ.mjs +6 -0
- package/dist/js/daemon-Co4CtpXZ.mjs +607 -0
- package/dist/js/{theme-CuQhvqzN.mjs → install-BE0gnaLl.mjs} +256 -186
- package/dist/js/{print-BZH0b3KB.mjs → print-CPCFci60.mjs} +2 -2
- package/dist/js/{print-Bin4u16d.mjs → print-Cd-bVSEJ.mjs} +224 -10
- package/dist/js/{print-share-D7OSxvE2.mjs → print-share-7qfpQtWK.mjs} +3 -3
- package/dist/js/{raw-pty-GAvxm2ol.mjs → raw-pty-BcjbTjHJ.mjs} +1 -1
- package/dist/js/raw-pty-ChUHav4d.mjs +5 -0
- package/dist/js/{rest-CamHVOce.mjs → rest-BlN_uWmL.mjs} +150 -19
- package/dist/js/rest-CDTXCmUb.mjs +4 -0
- package/package.json +3 -1
- package/dist/js/api-CDTKq_5Q.mjs +0 -145
- package/dist/js/client-D6NAkL9e.mjs +0 -6
- package/dist/js/raw-pty-2_VA1kw_.mjs +0 -5
- package/dist/js/rest-COkLEZOB.mjs +0 -4
- package/dist/js/util-CeisaZVY.mjs +0 -15
- /package/dist/js/{client-CpEvH2Pq.mjs → client-DfcJFEbh.mjs} +0 -0
- /package/dist/js/{output-B4cW10Ph.mjs → output-DYzzdXYV.mjs} +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
(boolean_scalar) @boolean
|
|
2
|
+
|
|
3
|
+
(null_scalar) @constant.builtin
|
|
4
|
+
|
|
5
|
+
[
|
|
6
|
+
(double_quote_scalar)
|
|
7
|
+
(single_quote_scalar)
|
|
8
|
+
(block_scalar)
|
|
9
|
+
(string_scalar)
|
|
10
|
+
] @string
|
|
11
|
+
|
|
12
|
+
[
|
|
13
|
+
(integer_scalar)
|
|
14
|
+
(float_scalar)
|
|
15
|
+
] @number
|
|
16
|
+
|
|
17
|
+
(comment) @comment
|
|
18
|
+
|
|
19
|
+
[
|
|
20
|
+
(anchor_name)
|
|
21
|
+
(alias_name)
|
|
22
|
+
] @label
|
|
23
|
+
|
|
24
|
+
(tag) @type
|
|
25
|
+
|
|
26
|
+
[
|
|
27
|
+
(yaml_directive)
|
|
28
|
+
(tag_directive)
|
|
29
|
+
(reserved_directive)
|
|
30
|
+
] @attribute
|
|
31
|
+
|
|
32
|
+
(block_mapping_pair
|
|
33
|
+
key: (flow_node
|
|
34
|
+
[
|
|
35
|
+
(double_quote_scalar)
|
|
36
|
+
(single_quote_scalar)
|
|
37
|
+
] @property))
|
|
38
|
+
|
|
39
|
+
(block_mapping_pair
|
|
40
|
+
key: (flow_node
|
|
41
|
+
(plain_scalar
|
|
42
|
+
(string_scalar) @property)))
|
|
43
|
+
|
|
44
|
+
(flow_mapping
|
|
45
|
+
(_
|
|
46
|
+
key: (flow_node
|
|
47
|
+
[
|
|
48
|
+
(double_quote_scalar)
|
|
49
|
+
(single_quote_scalar)
|
|
50
|
+
] @property)))
|
|
51
|
+
|
|
52
|
+
(flow_mapping
|
|
53
|
+
(_
|
|
54
|
+
key: (flow_node
|
|
55
|
+
(plain_scalar
|
|
56
|
+
(string_scalar) @property))))
|
|
57
|
+
|
|
58
|
+
[
|
|
59
|
+
","
|
|
60
|
+
"-"
|
|
61
|
+
":"
|
|
62
|
+
">"
|
|
63
|
+
"?"
|
|
64
|
+
"|"
|
|
65
|
+
] @punctuation.delimiter
|
|
66
|
+
|
|
67
|
+
[
|
|
68
|
+
"["
|
|
69
|
+
"]"
|
|
70
|
+
"{"
|
|
71
|
+
"}"
|
|
72
|
+
] @punctuation.bracket
|
|
73
|
+
|
|
74
|
+
[
|
|
75
|
+
"*"
|
|
76
|
+
"&"
|
|
77
|
+
"---"
|
|
78
|
+
"..."
|
|
79
|
+
] @punctuation.special
|
|
Binary file
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extra tree-sitter grammars for the chat TUI, on top of OpenTUI's five
|
|
3
|
+
* bundled ones (javascript, typescript, markdown, zig). Without these, every
|
|
4
|
+
* fenced code block and every diff/file-review pane in a non-js/ts language
|
|
5
|
+
* renders unhighlighted.
|
|
6
|
+
*
|
|
7
|
+
* Each entry pairs a prebuilt `.wasm` parser with its co-versioned
|
|
8
|
+
* `highlights.scm` query. `scripts/fetch-grammars.ts` vendors both from the
|
|
9
|
+
* same npm package into ./assets and records the versions in manifest.json —
|
|
10
|
+
* grammar and query MUST match or `Query` compilation throws at runtime.
|
|
11
|
+
*
|
|
12
|
+
* ── Why the `with { type: 'file' }` imports ──────────────────────────────
|
|
13
|
+
* The chat TUI always runs under Bun (the `chat` command re-execs itself
|
|
14
|
+
* under Bun before it touches OpenTUI). Under Bun these imports resolve to a
|
|
15
|
+
* real filesystem path in dev, and `bun build --compile` embeds each asset
|
|
16
|
+
* into the binary's virtual filesystem and rewrites the path to `/$bunfs`.
|
|
17
|
+
* OpenTUI loads its own bundled grammars exactly this way.
|
|
18
|
+
*
|
|
19
|
+
* Rolldown (tsdown, the npm build) CANNOT parse `.wasm`/`.scm` as modules, so
|
|
20
|
+
* this file is externalized in tsdown.config.mts and copied verbatim into
|
|
21
|
+
* dist/js/chat/tui/chunks/grammars/ alongside its assets; the bundled
|
|
22
|
+
* bin.mjs reaches it through a dynamic import that Bun resolves at runtime.
|
|
23
|
+
* Keep every asset reference a static `with { type: 'file' }` import so the
|
|
24
|
+
* compiled binary discovers and embeds them — a runtime `Bun.file(path)` on
|
|
25
|
+
* a computed path would not be embedded.
|
|
26
|
+
*/
|
|
27
|
+
import type { FiletypeParserOptions } from '@opentui/core';
|
|
28
|
+
|
|
29
|
+
import python_wasm from './assets/python.wasm' with { type: 'file' };
|
|
30
|
+
import python_scm from './assets/python.highlights.scm' with { type: 'file' };
|
|
31
|
+
import bash_wasm from './assets/bash.wasm' with { type: 'file' };
|
|
32
|
+
import bash_scm from './assets/bash.highlights.scm' with { type: 'file' };
|
|
33
|
+
import go_wasm from './assets/go.wasm' with { type: 'file' };
|
|
34
|
+
import go_scm from './assets/go.highlights.scm' with { type: 'file' };
|
|
35
|
+
import rust_wasm from './assets/rust.wasm' with { type: 'file' };
|
|
36
|
+
import rust_scm from './assets/rust.highlights.scm' with { type: 'file' };
|
|
37
|
+
import c_wasm from './assets/c.wasm' with { type: 'file' };
|
|
38
|
+
import c_scm from './assets/c.highlights.scm' with { type: 'file' };
|
|
39
|
+
import cpp_wasm from './assets/cpp.wasm' with { type: 'file' };
|
|
40
|
+
import cpp_scm from './assets/cpp.highlights.scm' with { type: 'file' };
|
|
41
|
+
import java_wasm from './assets/java.wasm' with { type: 'file' };
|
|
42
|
+
import java_scm from './assets/java.highlights.scm' with { type: 'file' };
|
|
43
|
+
import ruby_wasm from './assets/ruby.wasm' with { type: 'file' };
|
|
44
|
+
import ruby_scm from './assets/ruby.highlights.scm' with { type: 'file' };
|
|
45
|
+
import css_wasm from './assets/css.wasm' with { type: 'file' };
|
|
46
|
+
import css_scm from './assets/css.highlights.scm' with { type: 'file' };
|
|
47
|
+
import html_wasm from './assets/html.wasm' with { type: 'file' };
|
|
48
|
+
import html_scm from './assets/html.highlights.scm' with { type: 'file' };
|
|
49
|
+
import json_wasm from './assets/json.wasm' with { type: 'file' };
|
|
50
|
+
import json_scm from './assets/json.highlights.scm' with { type: 'file' };
|
|
51
|
+
import yaml_wasm from './assets/yaml.wasm' with { type: 'file' };
|
|
52
|
+
import yaml_scm from './assets/yaml.highlights.scm' with { type: 'file' };
|
|
53
|
+
import toml_wasm from './assets/toml.wasm' with { type: 'file' };
|
|
54
|
+
import toml_scm from './assets/toml.highlights.scm' with { type: 'file' };
|
|
55
|
+
import lua_wasm from './assets/lua.wasm' with { type: 'file' };
|
|
56
|
+
import lua_scm from './assets/lua.highlights.scm' with { type: 'file' };
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* `aliases` are the alternate names a code fence or file extension may use
|
|
60
|
+
* for the language (```sh → bash, ```py → python). OpenTUI's markdown
|
|
61
|
+
* injection resolver looks up a fence's info-string against each parser's
|
|
62
|
+
* registered aliases, so listing them here is what makes ```sh highlight.
|
|
63
|
+
* The canonical `filetype` need not be repeated in its own alias list.
|
|
64
|
+
*/
|
|
65
|
+
export const EXTRA_GRAMMARS: FiletypeParserOptions[] = [
|
|
66
|
+
{
|
|
67
|
+
filetype: 'python',
|
|
68
|
+
aliases: ['py', 'python3'],
|
|
69
|
+
wasm: python_wasm,
|
|
70
|
+
queries: { highlights: [python_scm] },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
filetype: 'bash',
|
|
74
|
+
aliases: ['sh', 'shell', 'zsh', 'shellscript'],
|
|
75
|
+
wasm: bash_wasm,
|
|
76
|
+
queries: { highlights: [bash_scm] },
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
filetype: 'go',
|
|
80
|
+
aliases: ['golang'],
|
|
81
|
+
wasm: go_wasm,
|
|
82
|
+
queries: { highlights: [go_scm] },
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
filetype: 'rust',
|
|
86
|
+
aliases: ['rs'],
|
|
87
|
+
wasm: rust_wasm,
|
|
88
|
+
queries: { highlights: [rust_scm] },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
filetype: 'c',
|
|
92
|
+
aliases: ['h'],
|
|
93
|
+
wasm: c_wasm,
|
|
94
|
+
queries: { highlights: [c_scm] },
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
filetype: 'cpp',
|
|
98
|
+
aliases: ['c++', 'cc', 'cxx', 'hpp', 'hxx', 'hh'],
|
|
99
|
+
wasm: cpp_wasm,
|
|
100
|
+
queries: { highlights: [cpp_scm] },
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
filetype: 'java',
|
|
104
|
+
wasm: java_wasm,
|
|
105
|
+
queries: { highlights: [java_scm] },
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
filetype: 'ruby',
|
|
109
|
+
aliases: ['rb'],
|
|
110
|
+
wasm: ruby_wasm,
|
|
111
|
+
queries: { highlights: [ruby_scm] },
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
filetype: 'css',
|
|
115
|
+
wasm: css_wasm,
|
|
116
|
+
queries: { highlights: [css_scm] },
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
filetype: 'html',
|
|
120
|
+
aliases: ['htm'],
|
|
121
|
+
wasm: html_wasm,
|
|
122
|
+
queries: { highlights: [html_scm] },
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
filetype: 'json',
|
|
126
|
+
aliases: ['jsonc'],
|
|
127
|
+
wasm: json_wasm,
|
|
128
|
+
queries: { highlights: [json_scm] },
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
filetype: 'yaml',
|
|
132
|
+
aliases: ['yml'],
|
|
133
|
+
wasm: yaml_wasm,
|
|
134
|
+
queries: { highlights: [yaml_scm] },
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
filetype: 'toml',
|
|
138
|
+
wasm: toml_wasm,
|
|
139
|
+
queries: { highlights: [toml_scm] },
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
filetype: 'lua',
|
|
143
|
+
wasm: lua_wasm,
|
|
144
|
+
queries: { highlights: [lua_scm] },
|
|
145
|
+
},
|
|
146
|
+
];
|
|
@@ -1,10 +1,66 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { a as errorMessage, t as HttpError } from "./rest-BlN_uWmL.mjs";
|
|
3
|
+
import os from "node:os";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { WebSocket } from "ws";
|
|
7
7
|
|
|
8
|
+
//#region src/chat/portal/machine.ts
|
|
9
|
+
/**
|
|
10
|
+
* Identity this machine registers under when the CLI shares it via the portal.
|
|
11
|
+
*
|
|
12
|
+
* The `-cli` suffix / `(CLI)` label keep a CLI-shared machine a DISTINCT portal
|
|
13
|
+
* device from the same host's Skydive Desktop app. `portal_device` is unique on
|
|
14
|
+
* (org, user, machineName), and directives route to whichever socket holds the
|
|
15
|
+
* device — if the CLI and desktop registered the same name they'd share a
|
|
16
|
+
* device row and both execute every directive. Distinct names also make the
|
|
17
|
+
* grant UI unambiguous about which surface is being authorized.
|
|
18
|
+
*/
|
|
19
|
+
function machineIdentity() {
|
|
20
|
+
const host = (os.hostname() || "machine").trim().replace(/\.local$/i, "") || "machine";
|
|
21
|
+
return {
|
|
22
|
+
machineName: `${host}-cli`,
|
|
23
|
+
friendlyName: `${host} (CLI)`
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const INHERITED_ENV = [
|
|
27
|
+
"HOME",
|
|
28
|
+
"USER",
|
|
29
|
+
"LOGNAME",
|
|
30
|
+
"SHELL",
|
|
31
|
+
"LANG",
|
|
32
|
+
"LC_ALL",
|
|
33
|
+
"TMPDIR",
|
|
34
|
+
"TERM",
|
|
35
|
+
"PATH"
|
|
36
|
+
];
|
|
37
|
+
function buildEnv(extra) {
|
|
38
|
+
const env = {};
|
|
39
|
+
for (const key of INHERITED_ENV) {
|
|
40
|
+
const value = process.env[key];
|
|
41
|
+
if (value !== void 0) env[key] = value;
|
|
42
|
+
}
|
|
43
|
+
if (extra) for (const [key, value] of Object.entries(extra)) env[key] = value;
|
|
44
|
+
return env;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build the desktop-portal WebSocket URL from the chat origin. Mirrors the Rust
|
|
48
|
+
* desktop client: http→ws, https→wss, scheme-less defaults to wss, and the
|
|
49
|
+
* machine/label ride as query pairs (percent-encoded by URL).
|
|
50
|
+
*/
|
|
51
|
+
function portalWsUrl(appUrl, machine, label) {
|
|
52
|
+
const base = appUrl.replace(/\/+$/, "");
|
|
53
|
+
let wsBase;
|
|
54
|
+
if (base.startsWith("https://")) wsBase = `wss://${base.slice(8)}`;
|
|
55
|
+
else if (base.startsWith("http://")) wsBase = `ws://${base.slice(7)}`;
|
|
56
|
+
else wsBase = `wss://${base}`;
|
|
57
|
+
const url = new URL(`${wsBase}/api/v1/portal/desktop`);
|
|
58
|
+
url.searchParams.set("machine", machine);
|
|
59
|
+
url.searchParams.set("label", label);
|
|
60
|
+
return url.toString();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
8
64
|
//#region ../portal-protocol/src/index.ts
|
|
9
65
|
const MAX_WS_FRAME_BYTES = 16 * 1024 * 1024;
|
|
10
66
|
const T_DATA = 1;
|
|
@@ -31,7 +87,8 @@ const ctrlMessageSchema = z.discriminatedUnion("t", [
|
|
|
31
87
|
z.object({
|
|
32
88
|
t: z.literal("open"),
|
|
33
89
|
argv: z.array(z.string()),
|
|
34
|
-
env: z.record(z.string()).nullable()
|
|
90
|
+
env: z.record(z.string()).nullable(),
|
|
91
|
+
conversationId: z.string().nullable().optional()
|
|
35
92
|
}),
|
|
36
93
|
z.object({ t: z.literal("stdin_eof") }),
|
|
37
94
|
z.object({ t: z.literal("pause") }),
|
|
@@ -103,7 +160,7 @@ var JobManager = class {
|
|
|
103
160
|
handleCtrl(id, msg) {
|
|
104
161
|
switch (msg.t) {
|
|
105
162
|
case "open":
|
|
106
|
-
this.startJob(id, msg.argv, msg.env);
|
|
163
|
+
this.startJob(id, msg.argv, msg.env, msg.conversationId ?? null);
|
|
107
164
|
return;
|
|
108
165
|
case "stdin_eof":
|
|
109
166
|
this.jobs.get(id)?.child.stdin.end();
|
|
@@ -133,7 +190,7 @@ var JobManager = class {
|
|
|
133
190
|
job.child.stderr.resume();
|
|
134
191
|
}
|
|
135
192
|
}
|
|
136
|
-
startJob(id, argv, env) {
|
|
193
|
+
startJob(id, argv, env, conversationId) {
|
|
137
194
|
const [program, ...args] = argv;
|
|
138
195
|
if (!program) {
|
|
139
196
|
this.opts.send(encodeCtrl(id, {
|
|
@@ -145,7 +202,7 @@ var JobManager = class {
|
|
|
145
202
|
let child;
|
|
146
203
|
try {
|
|
147
204
|
child = spawn(program, args, {
|
|
148
|
-
cwd: this.opts.
|
|
205
|
+
cwd: this.opts.resolveCwd(conversationId),
|
|
149
206
|
env: buildEnv(env),
|
|
150
207
|
stdio: [
|
|
151
208
|
"pipe",
|
|
@@ -194,6 +251,89 @@ var JobManager = class {
|
|
|
194
251
|
}
|
|
195
252
|
};
|
|
196
253
|
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/chat/portal/api.ts
|
|
256
|
+
/**
|
|
257
|
+
* The portal's session-authed REST surface, shared by `PortalClient` (the
|
|
258
|
+
* TUI/`portal open` connection) and the `skydive portal` management
|
|
259
|
+
* commands, so the endpoint contracts and response schemas live in exactly
|
|
260
|
+
* one place.
|
|
261
|
+
*/
|
|
262
|
+
const deviceSchema = z.object({
|
|
263
|
+
id: z.string(),
|
|
264
|
+
machineName: z.string(),
|
|
265
|
+
friendlyName: z.string(),
|
|
266
|
+
connected: z.boolean(),
|
|
267
|
+
lastSeen: z.string().nullable(),
|
|
268
|
+
grantedAgentIds: z.array(z.string())
|
|
269
|
+
});
|
|
270
|
+
const devicesResponseSchema = z.object({
|
|
271
|
+
devices: z.array(deviceSchema),
|
|
272
|
+
agents: z.array(z.object({
|
|
273
|
+
id: z.string(),
|
|
274
|
+
name: z.string()
|
|
275
|
+
}))
|
|
276
|
+
});
|
|
277
|
+
const deviceTokenSchema = z.object({ token: z.string().min(1) });
|
|
278
|
+
async function portalFetch(auth, path, init) {
|
|
279
|
+
const res = await fetch(`${auth.appUrl}${path}`, {
|
|
280
|
+
method: init.method,
|
|
281
|
+
headers: {
|
|
282
|
+
authorization: `Bearer ${auth.sessionToken}`,
|
|
283
|
+
accept: "application/json",
|
|
284
|
+
...init.body ? { "content-type": "application/json" } : {}
|
|
285
|
+
},
|
|
286
|
+
...init.body ? { body: init.body } : {}
|
|
287
|
+
});
|
|
288
|
+
if (!res.ok) {
|
|
289
|
+
const body = await res.text().catch(() => "");
|
|
290
|
+
throw new HttpError(res.status, body);
|
|
291
|
+
}
|
|
292
|
+
return res.json();
|
|
293
|
+
}
|
|
294
|
+
async function fetchPortalDevices(auth) {
|
|
295
|
+
const json = await portalFetch(auth, "/api/v1/portal/devices", { method: "GET" });
|
|
296
|
+
return devicesResponseSchema.parse(json);
|
|
297
|
+
}
|
|
298
|
+
const registerResponseSchema = z.object({ device: z.object({ id: z.string() }) });
|
|
299
|
+
/**
|
|
300
|
+
* Register this machine's device row without connecting. Connecting registers
|
|
301
|
+
* as a side effect; this covers granting an agent on a machine that has never
|
|
302
|
+
* shared yet (the grant references the device row).
|
|
303
|
+
*/
|
|
304
|
+
async function registerPortalDevice(auth, { machineName, friendlyName }) {
|
|
305
|
+
const json = await portalFetch(auth, "/api/v1/portal/devices", {
|
|
306
|
+
method: "POST",
|
|
307
|
+
body: JSON.stringify({
|
|
308
|
+
machineName,
|
|
309
|
+
friendlyName
|
|
310
|
+
})
|
|
311
|
+
});
|
|
312
|
+
return registerResponseSchema.parse(json).device;
|
|
313
|
+
}
|
|
314
|
+
/** Short-lived token the machine presents when dialing the portal WebSocket. */
|
|
315
|
+
async function mintPortalDeviceToken(auth) {
|
|
316
|
+
const json = await portalFetch(auth, "/api/v1/portal/device-token", { method: "POST" });
|
|
317
|
+
return deviceTokenSchema.parse(json).token;
|
|
318
|
+
}
|
|
319
|
+
async function grantPortalAccess(auth, { deviceId, agentId }) {
|
|
320
|
+
await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants`, {
|
|
321
|
+
method: "POST",
|
|
322
|
+
body: JSON.stringify({ agentId })
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
async function revokePortalAccess(auth, { deviceId, agentId }) {
|
|
326
|
+
await portalFetch(auth, `/api/v1/portal/devices/${encodeURIComponent(deviceId)}/grants/${encodeURIComponent(agentId)}`, { method: "DELETE" });
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* The device row for a given machine identity. Matching is by `machineName`
|
|
330
|
+
* equality — the stable per-surface handle (`<host>-cli` vs the desktop's
|
|
331
|
+
* `<host>`), not the display label.
|
|
332
|
+
*/
|
|
333
|
+
function findThisDevice(devices, machineName) {
|
|
334
|
+
return devices.find((device) => device.machineName === machineName) ?? null;
|
|
335
|
+
}
|
|
336
|
+
|
|
197
337
|
//#endregion
|
|
198
338
|
//#region src/chat/portal/client.ts
|
|
199
339
|
const INITIAL_BACKOFF_MS = 500;
|
|
@@ -309,7 +449,7 @@ var PortalClient = class {
|
|
|
309
449
|
});
|
|
310
450
|
this.ws = ws;
|
|
311
451
|
const jobs = new JobManager({
|
|
312
|
-
|
|
452
|
+
resolveCwd: this.opts.resolveCwd,
|
|
313
453
|
send: (frame) => {
|
|
314
454
|
if (ws.readyState === WebSocket.OPEN) ws.send(frame);
|
|
315
455
|
}
|
|
@@ -363,4 +503,4 @@ function sleep(ms) {
|
|
|
363
503
|
}
|
|
364
504
|
|
|
365
505
|
//#endregion
|
|
366
|
-
export { PortalClient as t };
|
|
506
|
+
export { registerPortalDevice as a, grantPortalAccess as i, fetchPortalDevices as n, revokePortalAccess as o, findThisDevice as r, machineIdentity as s, PortalClient as t };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./rest-BlN_uWmL.mjs";
|
|
3
|
+
import "./client-Dc7GZ3PG.mjs";
|
|
4
|
+
import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as stopDaemon, r as isDaemonListening, t as PortalDaemon } from "./daemon-Co4CtpXZ.mjs";
|
|
5
|
+
|
|
6
|
+
export { runPortalDaemon };
|