@openma/cli 0.4.0 → 0.5.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/LICENSE +201 -21
- package/dist/index.js +419 -48
- package/package.json +2 -2
package/LICENSE
CHANGED
|
@@ -1,21 +1,201 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for describing the origin of the Work and
|
|
141
|
+
reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Support. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or support.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2025 Open Managed Agents Contributors
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/dist/index.js
CHANGED
|
@@ -94,13 +94,59 @@ import { mkdir, readFile, writeFile, chmod } from "node:fs/promises";
|
|
|
94
94
|
import { dirname } from "node:path";
|
|
95
95
|
import { randomUUID } from "node:crypto";
|
|
96
96
|
async function readCreds() {
|
|
97
|
+
let text;
|
|
97
98
|
try {
|
|
98
|
-
|
|
99
|
-
return JSON.parse(text);
|
|
99
|
+
text = await readFile(paths().credsFile, "utf-8");
|
|
100
100
|
} catch (e) {
|
|
101
101
|
if (e.code === "ENOENT") return null;
|
|
102
102
|
throw e;
|
|
103
103
|
}
|
|
104
|
+
const parsed = JSON.parse(text);
|
|
105
|
+
if (parsed.v === 2 && Array.isArray(parsed.tenants)) {
|
|
106
|
+
return parsed;
|
|
107
|
+
}
|
|
108
|
+
const v1 = parsed;
|
|
109
|
+
const v2 = await migrateV1ToV2(v1);
|
|
110
|
+
try {
|
|
111
|
+
await writeCreds(v2);
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
return v2;
|
|
115
|
+
}
|
|
116
|
+
async function migrateV1ToV2(v1) {
|
|
117
|
+
const stubKey = v1.agentApiKey ?? "";
|
|
118
|
+
let tenants;
|
|
119
|
+
try {
|
|
120
|
+
const url2 = `${v1.serverUrl.replace(/\/$/, "")}/agents/runtime/me`;
|
|
121
|
+
const res = await fetch(url2, {
|
|
122
|
+
headers: { Authorization: `Bearer ${v1.token}` }
|
|
123
|
+
});
|
|
124
|
+
if (!res.ok) {
|
|
125
|
+
throw new Error(`HTTP ${res.status}`);
|
|
126
|
+
}
|
|
127
|
+
const body = await res.json();
|
|
128
|
+
if (!Array.isArray(body.tenants) || body.tenants.length === 0) {
|
|
129
|
+
throw new Error("server returned no tenants");
|
|
130
|
+
}
|
|
131
|
+
tenants = body.tenants.map((t) => ({
|
|
132
|
+
id: t.id,
|
|
133
|
+
name: t.name,
|
|
134
|
+
agentApiKey: stubKey
|
|
135
|
+
}));
|
|
136
|
+
} catch {
|
|
137
|
+
tenants = [
|
|
138
|
+
{ id: UNKNOWN_TENANT_ID, name: "Unknown workspace", agentApiKey: stubKey }
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
v: 2,
|
|
143
|
+
serverUrl: v1.serverUrl,
|
|
144
|
+
runtimeId: v1.runtimeId,
|
|
145
|
+
token: v1.token,
|
|
146
|
+
tenants,
|
|
147
|
+
machineId: v1.machineId,
|
|
148
|
+
createdAt: v1.createdAt
|
|
149
|
+
};
|
|
104
150
|
}
|
|
105
151
|
async function writeCreds(creds) {
|
|
106
152
|
const file2 = paths().credsFile;
|
|
@@ -129,10 +175,12 @@ async function getOrCreateMachineId() {
|
|
|
129
175
|
await writeFile(file2, id + "\n", { mode: 384 });
|
|
130
176
|
return id;
|
|
131
177
|
}
|
|
178
|
+
var UNKNOWN_TENANT_ID;
|
|
132
179
|
var init_config = __esm({
|
|
133
180
|
"src/bridge/lib/config.ts"() {
|
|
134
181
|
"use strict";
|
|
135
182
|
init_platform();
|
|
183
|
+
UNKNOWN_TENANT_ID = "__unknown__";
|
|
136
184
|
}
|
|
137
185
|
});
|
|
138
186
|
|
|
@@ -7103,6 +7151,33 @@ function serviceInstallOpts() {
|
|
|
7103
7151
|
};
|
|
7104
7152
|
}
|
|
7105
7153
|
async function runSetup(opts) {
|
|
7154
|
+
if (process.env.OMA_DEBUG_HANDLES) {
|
|
7155
|
+
let tick = 0;
|
|
7156
|
+
const probe = setInterval(() => {
|
|
7157
|
+
tick += 1;
|
|
7158
|
+
const handles = process._getActiveHandles();
|
|
7159
|
+
const requests = process._getActiveRequests();
|
|
7160
|
+
const hsum = handles.map((h) => h?.constructor?.name ?? "?").reduce((a, k) => {
|
|
7161
|
+
a[k] = (a[k] ?? 0) + 1;
|
|
7162
|
+
return a;
|
|
7163
|
+
}, {});
|
|
7164
|
+
const rsum = requests.map((r) => r?.constructor?.name ?? "?").reduce((a, k) => {
|
|
7165
|
+
a[k] = (a[k] ?? 0) + 1;
|
|
7166
|
+
return a;
|
|
7167
|
+
}, {});
|
|
7168
|
+
process.stderr.write(`
|
|
7169
|
+
[debug t=${tick * 2}s] handles: ${JSON.stringify(hsum)} requests: ${JSON.stringify(rsum)}
|
|
7170
|
+
`);
|
|
7171
|
+
}, 2e3);
|
|
7172
|
+
probe.unref();
|
|
7173
|
+
}
|
|
7174
|
+
try {
|
|
7175
|
+
await runSetupInner(opts);
|
|
7176
|
+
} finally {
|
|
7177
|
+
setImmediate(() => process.exit(0));
|
|
7178
|
+
}
|
|
7179
|
+
}
|
|
7180
|
+
async function runSetupInner(opts) {
|
|
7106
7181
|
const profile = currentProfile();
|
|
7107
7182
|
const profileTag = profile ? ` [profile=${profile}]` : "";
|
|
7108
7183
|
printBanner(`setup \u2014 register this machine with ${opts.serverUrl}${profileTag}`, PKG_VERSION);
|
|
@@ -7145,14 +7220,22 @@ ${c.bold("Up to date.")}
|
|
|
7145
7220
|
machine_id: machineId,
|
|
7146
7221
|
hostname: hostname(),
|
|
7147
7222
|
os: osTag(),
|
|
7148
|
-
version: PKG_VERSION
|
|
7223
|
+
version: PKG_VERSION,
|
|
7224
|
+
// Opt into the v2 multi-tenant response — server returns one entry
|
|
7225
|
+
// per (runtime, tenant) pair instead of a single legacy agent_api_key.
|
|
7226
|
+
multi_tenant: true
|
|
7149
7227
|
});
|
|
7150
|
-
log.ok(`runtime registered ${c.dim(exchange.runtime_id.slice(0, 8) + "\u2026")}`);
|
|
7228
|
+
log.ok(`runtime registered ${c.dim(exchange.runtime_id.slice(0, 8) + "\u2026")} (${exchange.tenants.length} workspaces authorized)`);
|
|
7151
7229
|
await writeCreds({
|
|
7230
|
+
v: 2,
|
|
7152
7231
|
serverUrl: opts.serverUrl,
|
|
7153
7232
|
runtimeId: exchange.runtime_id,
|
|
7154
7233
|
token: exchange.token,
|
|
7155
|
-
|
|
7234
|
+
tenants: exchange.tenants.map((t) => ({
|
|
7235
|
+
id: t.id,
|
|
7236
|
+
name: t.name,
|
|
7237
|
+
agentApiKey: t.agent_api_key
|
|
7238
|
+
})),
|
|
7156
7239
|
machineId,
|
|
7157
7240
|
createdAt: Math.floor(Date.now() / 1e3)
|
|
7158
7241
|
});
|
|
@@ -7238,8 +7321,12 @@ async function installAndReport(opts) {
|
|
|
7238
7321
|
}
|
|
7239
7322
|
process.stderr.write("\n");
|
|
7240
7323
|
process.stderr.write(`${c.bold("Done.")} the runtime should appear online at ${c.cyan(opts.browserOrigin)}
|
|
7241
|
-
|
|
7242
7324
|
`);
|
|
7325
|
+
process.stderr.write(
|
|
7326
|
+
`${c.dim(" After joining new workspaces, run `oma bridge refresh` to authorize them on this daemon.")}
|
|
7327
|
+
|
|
7328
|
+
`
|
|
7329
|
+
);
|
|
7243
7330
|
}
|
|
7244
7331
|
function execIntoDaemon() {
|
|
7245
7332
|
const profile = currentProfile();
|
|
@@ -21641,7 +21728,14 @@ var init_session_manager = __esm({
|
|
|
21641
21728
|
#spawner = new NodeSpawner();
|
|
21642
21729
|
#runtime = new AcpRuntimeImpl(this.#spawner);
|
|
21643
21730
|
#sessions = /* @__PURE__ */ new Map();
|
|
21644
|
-
#env = {
|
|
21731
|
+
#env = { apiUrl: "", runtimeToken: "" };
|
|
21732
|
+
/**
|
|
21733
|
+
* Per-tenant `oma_*` keys, keyed by tenant_id. Populated by the daemon
|
|
21734
|
+
* at startup from CredentialsV2.tenants and refreshed on SIGHUP after
|
|
21735
|
+
* `oma bridge refresh` rotates them server-side. Empty until the
|
|
21736
|
+
* daemon calls `setTenantKeys()`.
|
|
21737
|
+
*/
|
|
21738
|
+
#tenantKeys = /* @__PURE__ */ new Map();
|
|
21645
21739
|
/** Set by `drain()` to refuse new session.start while in-flight turns
|
|
21646
21740
|
* finish. Existing sessions keep accepting prompts so a user mid-turn
|
|
21647
21741
|
* doesn't hit "session not ready" mid-stream just because the daemon
|
|
@@ -21653,6 +21747,21 @@ var init_session_manager = __esm({
|
|
|
21653
21747
|
setSpawnEnv(env) {
|
|
21654
21748
|
this.#env = env;
|
|
21655
21749
|
}
|
|
21750
|
+
/**
|
|
21751
|
+
* Replace the in-memory map of tenant_id → `oma_*` PAT. Called once at
|
|
21752
|
+
* daemon startup from CredentialsV2.tenants, and again on SIGHUP after
|
|
21753
|
+
* the user runs `oma bridge refresh` (so newly-added tenants become
|
|
21754
|
+
* sessionable without a daemon restart, and revoked tenants stop being
|
|
21755
|
+
* accepted).
|
|
21756
|
+
*
|
|
21757
|
+
* Existing sessions are NOT torn down — a session pinned to a tenant
|
|
21758
|
+
* that was just removed keeps running until the harness disposes it.
|
|
21759
|
+
* The lookup is consulted per session.start, not per turn.
|
|
21760
|
+
*/
|
|
21761
|
+
setTenantKeys(tenants) {
|
|
21762
|
+
this.#tenantKeys.clear();
|
|
21763
|
+
for (const t of tenants) this.#tenantKeys.set(t.id, t.agentApiKey);
|
|
21764
|
+
}
|
|
21656
21765
|
setSender(send) {
|
|
21657
21766
|
this.#send = send;
|
|
21658
21767
|
}
|
|
@@ -21674,7 +21783,7 @@ var init_session_manager = __esm({
|
|
|
21674
21783
|
/** Re-announce alive sessions to the server (used after WS reconnect). */
|
|
21675
21784
|
announceAll() {
|
|
21676
21785
|
for (const [session_id, sess] of this.#sessions) {
|
|
21677
|
-
this.#send({ type: "session.ready", session_id, acp_session_id: sess.acpSessionId });
|
|
21786
|
+
this.#send({ type: "session.ready", session_id, tenant_id: sess.tenantId, acp_session_id: sess.acpSessionId });
|
|
21678
21787
|
}
|
|
21679
21788
|
}
|
|
21680
21789
|
async start(p) {
|
|
@@ -21687,11 +21796,38 @@ var init_session_manager = __esm({
|
|
|
21687
21796
|
});
|
|
21688
21797
|
return;
|
|
21689
21798
|
}
|
|
21799
|
+
let tenantId = p.tenant_id ?? "";
|
|
21800
|
+
let tenantKey = tenantId ? this.#tenantKeys.get(tenantId) : void 0;
|
|
21801
|
+
if (!tenantId) {
|
|
21802
|
+
const fallback = this.#tenantKeys.keys().next();
|
|
21803
|
+
if (!fallback.done) {
|
|
21804
|
+
tenantId = fallback.value;
|
|
21805
|
+
tenantKey = this.#tenantKeys.get(tenantId);
|
|
21806
|
+
process.stderr.write(
|
|
21807
|
+
` ! session.start missing tenant_id; falling back to first tenant ${tenantId.slice(0, 8)}\u2026
|
|
21808
|
+
`
|
|
21809
|
+
);
|
|
21810
|
+
}
|
|
21811
|
+
}
|
|
21812
|
+
if (!tenantKey) {
|
|
21813
|
+
this.#send({
|
|
21814
|
+
type: "session.error",
|
|
21815
|
+
session_id: p.session_id,
|
|
21816
|
+
tenant_id: p.tenant_id,
|
|
21817
|
+
message: `Tenant ${p.tenant_id ?? "(unspecified)"} not authorized for this runtime \u2014 run 'oma bridge refresh'`
|
|
21818
|
+
});
|
|
21819
|
+
return;
|
|
21820
|
+
}
|
|
21821
|
+
process.stderr.write(
|
|
21822
|
+
` \u2192 tenant-key selected sid=${p.session_id.slice(0, 8)} tenant=${tenantId.slice(0, 14)}
|
|
21823
|
+
`
|
|
21824
|
+
);
|
|
21690
21825
|
const existing = this.#sessions.get(p.session_id);
|
|
21691
21826
|
if (existing) {
|
|
21692
21827
|
this.#send({
|
|
21693
21828
|
type: "session.ready",
|
|
21694
21829
|
session_id: p.session_id,
|
|
21830
|
+
tenant_id: existing.tenantId,
|
|
21695
21831
|
acp_session_id: existing.acpSessionId
|
|
21696
21832
|
});
|
|
21697
21833
|
return;
|
|
@@ -21759,7 +21895,7 @@ var init_session_manager = __esm({
|
|
|
21759
21895
|
type: s.type,
|
|
21760
21896
|
name: s.name,
|
|
21761
21897
|
url: s.url,
|
|
21762
|
-
headers:
|
|
21898
|
+
headers: tenantKey ? [{ name: "Authorization", value: `Bearer ${tenantKey}` }] : []
|
|
21763
21899
|
}));
|
|
21764
21900
|
process.stderr.write(
|
|
21765
21901
|
` \u2192 SessionManager.start ${agent.spec.command} cwd=${sessionCwd}` + (extraEnv.CLAUDE_CONFIG_DIR ? ` cfg=${extraEnv.CLAUDE_CONFIG_DIR}` : "") + (blocklist.length ? ` blocklist=${blocklist.length}` : "") + (mcpServersForAcp.length ? ` mcp=${mcpServersForAcp.length}` : "") + (bundleEnv.length ? ` env=${bundleEnv.length}` : "") + // Intentionally NOT logging env names or values — these come from
|
|
@@ -21780,17 +21916,20 @@ var init_session_manager = __esm({
|
|
|
21780
21916
|
this.#sessions.set(p.session_id, {
|
|
21781
21917
|
acp: session,
|
|
21782
21918
|
acpSessionId: session.acpSessionId,
|
|
21919
|
+
tenantId,
|
|
21783
21920
|
turns: /* @__PURE__ */ new Map()
|
|
21784
21921
|
});
|
|
21785
21922
|
this.#send({
|
|
21786
21923
|
type: "session.ready",
|
|
21787
21924
|
session_id: p.session_id,
|
|
21925
|
+
tenant_id: tenantId,
|
|
21788
21926
|
acp_session_id: session.acpSessionId
|
|
21789
21927
|
});
|
|
21790
21928
|
} catch (e) {
|
|
21791
21929
|
this.#send({
|
|
21792
21930
|
type: "session.error",
|
|
21793
21931
|
session_id: p.session_id,
|
|
21932
|
+
tenant_id: tenantId,
|
|
21794
21933
|
message: e instanceof Error ? e.message : String(e)
|
|
21795
21934
|
});
|
|
21796
21935
|
}
|
|
@@ -21801,6 +21940,7 @@ var init_session_manager = __esm({
|
|
|
21801
21940
|
this.#send({
|
|
21802
21941
|
type: "session.error",
|
|
21803
21942
|
session_id: p.session_id,
|
|
21943
|
+
tenant_id: p.tenant_id,
|
|
21804
21944
|
turn_id: p.turn_id,
|
|
21805
21945
|
message: "no such session"
|
|
21806
21946
|
});
|
|
@@ -21821,6 +21961,7 @@ var init_session_manager = __esm({
|
|
|
21821
21961
|
this.#send({
|
|
21822
21962
|
type: "session.event",
|
|
21823
21963
|
session_id: p.session_id,
|
|
21964
|
+
tenant_id: sess.tenantId,
|
|
21824
21965
|
turn_id: p.turn_id,
|
|
21825
21966
|
event: ev
|
|
21826
21967
|
});
|
|
@@ -21829,16 +21970,18 @@ var init_session_manager = __esm({
|
|
|
21829
21970
|
this.#send({
|
|
21830
21971
|
type: "session.error",
|
|
21831
21972
|
session_id: p.session_id,
|
|
21973
|
+
tenant_id: sess.tenantId,
|
|
21832
21974
|
turn_id: p.turn_id,
|
|
21833
21975
|
message: promptErr
|
|
21834
21976
|
});
|
|
21835
21977
|
} else {
|
|
21836
|
-
this.#send({ type: "session.complete", session_id: p.session_id, turn_id: p.turn_id });
|
|
21978
|
+
this.#send({ type: "session.complete", session_id: p.session_id, tenant_id: sess.tenantId, turn_id: p.turn_id });
|
|
21837
21979
|
}
|
|
21838
21980
|
} catch (e) {
|
|
21839
21981
|
this.#send({
|
|
21840
21982
|
type: "session.error",
|
|
21841
21983
|
session_id: p.session_id,
|
|
21984
|
+
tenant_id: sess.tenantId,
|
|
21842
21985
|
turn_id: p.turn_id,
|
|
21843
21986
|
message: e instanceof Error ? e.message : String(e)
|
|
21844
21987
|
});
|
|
@@ -21852,9 +21995,11 @@ var init_session_manager = __esm({
|
|
|
21852
21995
|
sess.turns.get(turn_id)?.abort();
|
|
21853
21996
|
}
|
|
21854
21997
|
async dispose(session_id) {
|
|
21998
|
+
const sess = this.#sessions.get(session_id);
|
|
21999
|
+
const tenantId = sess?.tenantId ?? "";
|
|
21855
22000
|
await this.#killChild(session_id);
|
|
21856
22001
|
await removeSessionCwd(session_id);
|
|
21857
|
-
this.#send({ type: "session.disposed", session_id });
|
|
22002
|
+
this.#send({ type: "session.disposed", session_id, tenant_id: tenantId });
|
|
21858
22003
|
}
|
|
21859
22004
|
/** Best-effort cleanup on daemon shutdown. KEEPS spawn cwds — sessions are
|
|
21860
22005
|
* still live at the platform; the daemon coming back tomorrow needs the
|
|
@@ -22218,8 +22363,15 @@ async function runDaemon() {
|
|
|
22218
22363
|
}
|
|
22219
22364
|
process.on("SIGHUP", () => {
|
|
22220
22365
|
void (async () => {
|
|
22221
|
-
log.step("SIGHUP \u2014 refreshing agent detection");
|
|
22366
|
+
log.step("SIGHUP \u2014 refreshing agent detection + credentials");
|
|
22222
22367
|
try {
|
|
22368
|
+
const freshCreds = await readCreds();
|
|
22369
|
+
if (freshCreds) {
|
|
22370
|
+
sessions.setTenantKeys(freshCreds.tenants);
|
|
22371
|
+
log.ok(`re-loaded credentials (${freshCreds.tenants.length} tenants)`);
|
|
22372
|
+
} else {
|
|
22373
|
+
log.warn("credentials file disappeared mid-SIGHUP; tenant keys unchanged");
|
|
22374
|
+
}
|
|
22223
22375
|
await loadRegistry({ cachePath, forceRefresh: true });
|
|
22224
22376
|
const agents = (await detectAll()).map((a) => ({ id: a.id, binary: a.spec.command }));
|
|
22225
22377
|
const localSkillsDetailed = await detectLocalSkills();
|
|
@@ -22251,10 +22403,10 @@ async function runDaemon() {
|
|
|
22251
22403
|
const sessions = new SessionManager(() => {
|
|
22252
22404
|
});
|
|
22253
22405
|
sessions.setSpawnEnv({
|
|
22254
|
-
apiKey: creds.agentApiKey ?? "",
|
|
22255
22406
|
apiUrl: creds.serverUrl,
|
|
22256
22407
|
runtimeToken: creds.token
|
|
22257
22408
|
});
|
|
22409
|
+
sessions.setTenantKeys(creds.tenants);
|
|
22258
22410
|
while (!stopping) {
|
|
22259
22411
|
try {
|
|
22260
22412
|
const ws = new wrapper_default(wsUrl, {
|
|
@@ -22569,22 +22721,221 @@ var init_agents = __esm({
|
|
|
22569
22721
|
}
|
|
22570
22722
|
});
|
|
22571
22723
|
|
|
22724
|
+
// src/bridge/commands/refresh.ts
|
|
22725
|
+
var refresh_exports = {};
|
|
22726
|
+
__export(refresh_exports, {
|
|
22727
|
+
runRefresh: () => runRefresh
|
|
22728
|
+
});
|
|
22729
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
22730
|
+
import { join as join10 } from "node:path";
|
|
22731
|
+
async function runRefresh() {
|
|
22732
|
+
const profile = currentProfile();
|
|
22733
|
+
const profileTag = profile ? ` [profile=${profile}]` : "";
|
|
22734
|
+
printBanner(`refresh \u2014 reconcile tenants${profileTag}`, PKG_VERSION);
|
|
22735
|
+
const creds = await readCreds();
|
|
22736
|
+
if (!creds) {
|
|
22737
|
+
log.err("not set up \u2014 run `oma bridge setup` first");
|
|
22738
|
+
log.hint(`looked for ${paths().credsFile}`);
|
|
22739
|
+
process.exit(2);
|
|
22740
|
+
}
|
|
22741
|
+
const url2 = `${creds.serverUrl.replace(/\/$/, "")}/agents/runtime/${encodeURIComponent(creds.runtimeId)}/refresh`;
|
|
22742
|
+
let response;
|
|
22743
|
+
try {
|
|
22744
|
+
const res = await fetch(url2, {
|
|
22745
|
+
method: "POST",
|
|
22746
|
+
headers: {
|
|
22747
|
+
Authorization: `Bearer ${creds.token}`,
|
|
22748
|
+
"content-type": "application/json"
|
|
22749
|
+
},
|
|
22750
|
+
body: "{}"
|
|
22751
|
+
});
|
|
22752
|
+
const text = await res.text();
|
|
22753
|
+
if (!res.ok) {
|
|
22754
|
+
log.err(`refresh failed: HTTP ${res.status}: ${text.slice(0, 200)}`);
|
|
22755
|
+
process.exit(1);
|
|
22756
|
+
}
|
|
22757
|
+
response = JSON.parse(text);
|
|
22758
|
+
} catch (e) {
|
|
22759
|
+
log.err(`refresh request failed: ${e.message}`);
|
|
22760
|
+
process.exit(1);
|
|
22761
|
+
}
|
|
22762
|
+
const prevIds = new Set(creds.tenants.map((t) => t.id));
|
|
22763
|
+
const nextIds = new Set(response.tenants.map((t) => t.id));
|
|
22764
|
+
const added = response.tenants.filter((t) => !prevIds.has(t.id));
|
|
22765
|
+
const removed = creds.tenants.filter((t) => !nextIds.has(t.id));
|
|
22766
|
+
const existing = response.tenants.filter((t) => prevIds.has(t.id));
|
|
22767
|
+
const updated = {
|
|
22768
|
+
...creds,
|
|
22769
|
+
tenants: response.tenants.map((t) => ({
|
|
22770
|
+
id: t.id,
|
|
22771
|
+
name: t.name,
|
|
22772
|
+
agentApiKey: t.agent_api_key
|
|
22773
|
+
}))
|
|
22774
|
+
};
|
|
22775
|
+
await writeCreds(updated);
|
|
22776
|
+
log.ok(`credentials written ${c.dim(paths().credsFile)}`);
|
|
22777
|
+
const fmtList = (xs) => xs.length === 0 ? "\u2014" : xs.map((t) => `${t.name} (${t.id.slice(0, 8)}\u2026)`).join(", ");
|
|
22778
|
+
process.stderr.write(
|
|
22779
|
+
`
|
|
22780
|
+
Updated: +${added.length} added, -${removed.length} removed, =${existing.length} existing
|
|
22781
|
+
`
|
|
22782
|
+
);
|
|
22783
|
+
if (added.length > 0) process.stderr.write(` ${c.dim("added :")} ${fmtList(added)}
|
|
22784
|
+
`);
|
|
22785
|
+
if (removed.length > 0) process.stderr.write(` ${c.dim("removed :")} ${fmtList(removed)}
|
|
22786
|
+
`);
|
|
22787
|
+
if (existing.length > 0) process.stderr.write(` ${c.dim("kept :")} ${fmtList(existing)}
|
|
22788
|
+
`);
|
|
22789
|
+
process.stderr.write("\n");
|
|
22790
|
+
const pidFile = join10(paths().configDir, "daemon.pid");
|
|
22791
|
+
let pid = 0;
|
|
22792
|
+
try {
|
|
22793
|
+
pid = parseInt(readFileSync2(pidFile, "utf-8").trim(), 10);
|
|
22794
|
+
if (!Number.isFinite(pid) || pid <= 0) pid = 0;
|
|
22795
|
+
if (pid) process.kill(pid, 0);
|
|
22796
|
+
} catch {
|
|
22797
|
+
pid = 0;
|
|
22798
|
+
}
|
|
22799
|
+
if (pid === 0) {
|
|
22800
|
+
log.hint(`no running daemon${profileTag}; new tenants will load on next \`oma bridge daemon\` start`);
|
|
22801
|
+
return;
|
|
22802
|
+
}
|
|
22803
|
+
try {
|
|
22804
|
+
process.kill(pid, "SIGHUP");
|
|
22805
|
+
log.ok(`daemon${profileTag} reloading credentials (pid ${pid})`);
|
|
22806
|
+
} catch (e) {
|
|
22807
|
+
log.warn(`SIGHUP to pid ${pid} failed: ${e.message}`);
|
|
22808
|
+
log.hint(`creds file is still updated; restart the daemon to apply`);
|
|
22809
|
+
process.exit(2);
|
|
22810
|
+
}
|
|
22811
|
+
}
|
|
22812
|
+
var init_refresh = __esm({
|
|
22813
|
+
"src/bridge/commands/refresh.ts"() {
|
|
22814
|
+
"use strict";
|
|
22815
|
+
init_config();
|
|
22816
|
+
init_platform();
|
|
22817
|
+
init_style();
|
|
22818
|
+
init_version();
|
|
22819
|
+
}
|
|
22820
|
+
});
|
|
22821
|
+
|
|
22822
|
+
// src/bridge/commands/profiles.ts
|
|
22823
|
+
var profiles_exports = {};
|
|
22824
|
+
__export(profiles_exports, {
|
|
22825
|
+
runProfilesList: () => runProfilesList
|
|
22826
|
+
});
|
|
22827
|
+
import { readdir as readdir3, readFile as readFile7 } from "node:fs/promises";
|
|
22828
|
+
import { homedir as homedir5 } from "node:os";
|
|
22829
|
+
import { join as join11 } from "node:path";
|
|
22830
|
+
async function runProfilesList() {
|
|
22831
|
+
printBanner("bridge profiles \u2014 installed daemon profiles", PKG_VERSION);
|
|
22832
|
+
const omaRoot = join11(homedir5(), ".oma");
|
|
22833
|
+
let entries;
|
|
22834
|
+
try {
|
|
22835
|
+
entries = await readdir3(omaRoot);
|
|
22836
|
+
} catch {
|
|
22837
|
+
process.stderr.write(c.dim(` (no profiles installed \u2014 ${omaRoot} doesn't exist)
|
|
22838
|
+
`));
|
|
22839
|
+
return;
|
|
22840
|
+
}
|
|
22841
|
+
const profiles = [];
|
|
22842
|
+
for (const name of entries.sort()) {
|
|
22843
|
+
if (name !== "bridge" && !name.startsWith("bridge-")) continue;
|
|
22844
|
+
const profile = name === "bridge" ? "" : name.slice("bridge-".length);
|
|
22845
|
+
profiles.push(await readProfile(profile, join11(omaRoot, name)));
|
|
22846
|
+
}
|
|
22847
|
+
if (profiles.length === 0) {
|
|
22848
|
+
process.stderr.write(c.dim(" (no profiles installed)\n"));
|
|
22849
|
+
process.stderr.write(` Run ${c.cyan("oma bridge setup")} to create one.
|
|
22850
|
+
`);
|
|
22851
|
+
return;
|
|
22852
|
+
}
|
|
22853
|
+
process.stderr.write(
|
|
22854
|
+
` ${c.dim("PROFILE")} ${c.dim("SERVER")} ${c.dim("RUNTIME")} ${c.dim("TENANTS")} ${c.dim("DAEMON")}
|
|
22855
|
+
`
|
|
22856
|
+
);
|
|
22857
|
+
for (const p of profiles) {
|
|
22858
|
+
const profileLabel = p.profile === "" ? c.bold("(default)") : c.bold(p.profile);
|
|
22859
|
+
const server = p.serverUrl ?? c.dim("\u2014");
|
|
22860
|
+
const runtime = p.runtimeId ? p.runtimeId.slice(0, 8) + "\u2026" : c.dim("\u2014");
|
|
22861
|
+
const tenants = p.tenantCount === null ? c.dim("\u2014") : p.credsVersion === 1 ? `1 ${c.dim("(v1 creds \u2014 pre-multi-tenant)")}` : String(p.tenantCount);
|
|
22862
|
+
const daemon = p.daemonPid ? p.daemonAlive ? `${c.green("\u25CF")} pid ${p.daemonPid}` : `${c.yellow("\u25CB")} pid ${p.daemonPid} ${c.dim("(stale)")}` : c.dim("\u2014");
|
|
22863
|
+
process.stderr.write(` ${profileLabel} ${server} ${runtime} ${tenants} ${daemon}
|
|
22864
|
+
`);
|
|
22865
|
+
}
|
|
22866
|
+
process.stderr.write(
|
|
22867
|
+
`
|
|
22868
|
+
${c.dim(
|
|
22869
|
+
"Switch active profile with `OMA_PROFILE=<name> oma bridge <cmd>` (or --profile=<name>)."
|
|
22870
|
+
)}
|
|
22871
|
+
`
|
|
22872
|
+
);
|
|
22873
|
+
}
|
|
22874
|
+
async function readProfile(profile, configDir) {
|
|
22875
|
+
const info = {
|
|
22876
|
+
profile,
|
|
22877
|
+
configDir,
|
|
22878
|
+
serverUrl: null,
|
|
22879
|
+
runtimeId: null,
|
|
22880
|
+
tenantCount: null,
|
|
22881
|
+
credsVersion: "unknown",
|
|
22882
|
+
daemonPid: null,
|
|
22883
|
+
daemonAlive: false
|
|
22884
|
+
};
|
|
22885
|
+
try {
|
|
22886
|
+
const raw = await readFile7(join11(configDir, "credentials.json"), "utf-8");
|
|
22887
|
+
const parsed = JSON.parse(raw);
|
|
22888
|
+
info.serverUrl = parsed.serverUrl ?? null;
|
|
22889
|
+
info.runtimeId = parsed.runtimeId ?? null;
|
|
22890
|
+
if (parsed.v === 2 && Array.isArray(parsed.tenants)) {
|
|
22891
|
+
info.credsVersion = 2;
|
|
22892
|
+
info.tenantCount = parsed.tenants.length;
|
|
22893
|
+
} else if (parsed.agentApiKey) {
|
|
22894
|
+
info.credsVersion = 1;
|
|
22895
|
+
info.tenantCount = 1;
|
|
22896
|
+
}
|
|
22897
|
+
} catch {
|
|
22898
|
+
}
|
|
22899
|
+
try {
|
|
22900
|
+
const pidStr = (await readFile7(join11(configDir, "daemon.pid"), "utf-8")).trim();
|
|
22901
|
+
const pid = parseInt(pidStr, 10);
|
|
22902
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
22903
|
+
info.daemonPid = pid;
|
|
22904
|
+
try {
|
|
22905
|
+
process.kill(pid, 0);
|
|
22906
|
+
info.daemonAlive = true;
|
|
22907
|
+
} catch {
|
|
22908
|
+
info.daemonAlive = false;
|
|
22909
|
+
}
|
|
22910
|
+
}
|
|
22911
|
+
} catch {
|
|
22912
|
+
}
|
|
22913
|
+
return info;
|
|
22914
|
+
}
|
|
22915
|
+
var init_profiles = __esm({
|
|
22916
|
+
"src/bridge/commands/profiles.ts"() {
|
|
22917
|
+
"use strict";
|
|
22918
|
+
init_style();
|
|
22919
|
+
init_version();
|
|
22920
|
+
}
|
|
22921
|
+
});
|
|
22922
|
+
|
|
22572
22923
|
// src/index.ts
|
|
22573
22924
|
init_platform();
|
|
22574
22925
|
import { createServer as createServer2 } from "node:http";
|
|
22575
22926
|
import { execSync } from "node:child_process";
|
|
22576
|
-
import { homedir as
|
|
22577
|
-
import { join as
|
|
22578
|
-
import { mkdirSync as mkdirSync3, readFileSync as
|
|
22927
|
+
import { homedir as homedir6, hostname as hostname4 } from "node:os";
|
|
22928
|
+
import { join as join12, dirname as dirname9 } from "node:path";
|
|
22929
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync3, existsSync as existsSync2, chmodSync as chmodSync2 } from "node:fs";
|
|
22579
22930
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
22580
22931
|
function credentialsPath() {
|
|
22581
22932
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
22582
|
-
const base = xdg && xdg.length > 0 ? xdg :
|
|
22933
|
+
const base = xdg && xdg.length > 0 ? xdg : join12(homedir6(), ".config");
|
|
22583
22934
|
const profile = currentProfile();
|
|
22584
22935
|
const file2 = profile ? `credentials.${profile}.json` : "credentials.json";
|
|
22585
|
-
return
|
|
22936
|
+
return join12(base, "oma", file2);
|
|
22586
22937
|
}
|
|
22587
|
-
function
|
|
22938
|
+
function migrateV1ToV22(v1) {
|
|
22588
22939
|
return {
|
|
22589
22940
|
version: 2,
|
|
22590
22941
|
base_url: v1.base_url,
|
|
@@ -22605,11 +22956,11 @@ function readCredentials() {
|
|
|
22605
22956
|
const path2 = credentialsPath();
|
|
22606
22957
|
try {
|
|
22607
22958
|
if (!existsSync2(path2)) return null;
|
|
22608
|
-
const raw =
|
|
22959
|
+
const raw = readFileSync3(path2, "utf8");
|
|
22609
22960
|
const parsed = JSON.parse(raw);
|
|
22610
22961
|
if (parsed.version === 2) return parsed;
|
|
22611
22962
|
if (parsed.version === 1) {
|
|
22612
|
-
const v2 =
|
|
22963
|
+
const v2 = migrateV1ToV22(parsed);
|
|
22613
22964
|
try {
|
|
22614
22965
|
writeCredentials(v2);
|
|
22615
22966
|
} catch {
|
|
@@ -22826,9 +23177,11 @@ async function streamChat(config2, sessionId, text) {
|
|
|
22826
23177
|
}
|
|
22827
23178
|
}
|
|
22828
23179
|
async function tailSession(config2, sessionId) {
|
|
22829
|
-
const res = await rawStream(
|
|
22830
|
-
|
|
22831
|
-
|
|
23180
|
+
const res = await rawStream(
|
|
23181
|
+
config2,
|
|
23182
|
+
`/v1/sessions/${sessionId}/events/stream?include=chunks&replay=1`,
|
|
23183
|
+
{ headers: { accept: "text/event-stream" } }
|
|
23184
|
+
);
|
|
22832
23185
|
for await (const ev of parseSSE(res)) {
|
|
22833
23186
|
process.stdout.write(JSON.stringify(ev) + "\n");
|
|
22834
23187
|
}
|
|
@@ -23177,7 +23530,7 @@ var commands = [
|
|
|
23177
23530
|
match: ["agents", "create"],
|
|
23178
23531
|
usage: "oma agents create <name> [--model <id>]",
|
|
23179
23532
|
desc: "Create agent",
|
|
23180
|
-
http: "POST /v1/agents {name, model, system, tools, skills?, mcp_servers?,
|
|
23533
|
+
http: "POST /v1/agents {name, model, system, tools, skills?, mcp_servers?, multiagent?, _oma?:{runtime_binding,harness,...}}",
|
|
23181
23534
|
async run(config2, args) {
|
|
23182
23535
|
const name = flag(args, "--name") || args.find((a) => !a.startsWith("--"));
|
|
23183
23536
|
const model = flag(args, "--model") || "claude-sonnet-4-6";
|
|
@@ -23196,8 +23549,10 @@ var commands = [
|
|
|
23196
23549
|
tools: [{ type: "agent_toolset_20260401" }]
|
|
23197
23550
|
};
|
|
23198
23551
|
if (useAcpProxy) {
|
|
23199
|
-
body.
|
|
23200
|
-
|
|
23552
|
+
body._oma = {
|
|
23553
|
+
harness: "acp-proxy",
|
|
23554
|
+
runtime_binding: { runtime_id: runtimeId, acp_agent_id: acpAgentId }
|
|
23555
|
+
};
|
|
23201
23556
|
}
|
|
23202
23557
|
const agent = await apiFetch(config2, "/v1/agents", { method: "POST", body: JSON.stringify(body) });
|
|
23203
23558
|
console.log(`Agent created: ${agent.name} (${agent.id})${useAcpProxy ? ` [acp-proxy \u2192 ${acpAgentId} on ${runtimeId.slice(0, 8)}\u2026]` : ""}`);
|
|
@@ -23407,27 +23762,27 @@ Version: v${a.version}`);
|
|
|
23407
23762
|
console.log("No model cards. Create one with: oma models create");
|
|
23408
23763
|
return;
|
|
23409
23764
|
}
|
|
23410
|
-
table([["
|
|
23765
|
+
table([["MODEL_ID", "PROVIDER", "WIRE MODEL", "KEY", "DEFAULT"], ...data.map((c3) => [c3.model_id, c3.provider, c3.model === c3.model_id ? "(same)" : c3.model, `****${c3.api_key_preview || ""}`, c3.is_default ? "yes" : ""])]);
|
|
23411
23766
|
}
|
|
23412
23767
|
},
|
|
23413
23768
|
{
|
|
23414
23769
|
group: "Model Cards",
|
|
23415
23770
|
match: ["models", "create"],
|
|
23416
|
-
usage: "oma models create --
|
|
23771
|
+
usage: "oma models create --model-id <id> --api-key <key> [--model <wire>] [--provider <p>]",
|
|
23417
23772
|
desc: "Create model card",
|
|
23418
|
-
http: "POST /v1/model_cards {
|
|
23773
|
+
http: "POST /v1/model_cards {model_id, provider, model?, api_key, base_url?, is_default?}",
|
|
23419
23774
|
async run(config2, args) {
|
|
23420
|
-
const name = flag(args, "--name");
|
|
23421
|
-
const provider = flag(args, "--provider") || "ant";
|
|
23422
23775
|
const modelId = flag(args, "--model-id");
|
|
23776
|
+
const provider = flag(args, "--provider") || "ant";
|
|
23777
|
+
const model = flag(args, "--model");
|
|
23423
23778
|
const apiKey = flag(args, "--api-key");
|
|
23424
23779
|
const baseUrl = flag(args, "--base-url");
|
|
23425
|
-
if (!
|
|
23426
|
-
console.error("Usage: oma models create --
|
|
23780
|
+
if (!modelId || !apiKey) {
|
|
23781
|
+
console.error("Usage: oma models create --model-id <id> --api-key <key> [--model <wire>] [--provider ant|oai|ant-compatible|oai-compatible] [--base-url <url>]");
|
|
23427
23782
|
process.exit(1);
|
|
23428
23783
|
}
|
|
23429
|
-
const card = await apiFetch(config2, "/v1/model_cards", { method: "POST", body: JSON.stringify({
|
|
23430
|
-
console.log(`Model card created: ${card.
|
|
23784
|
+
const card = await apiFetch(config2, "/v1/model_cards", { method: "POST", body: JSON.stringify({ model_id: modelId, provider, model, api_key: apiKey, base_url: baseUrl }) });
|
|
23785
|
+
console.log(`Model card created: ${card.model_id} (${card.id})`);
|
|
23431
23786
|
}
|
|
23432
23787
|
},
|
|
23433
23788
|
// API Keys
|
|
@@ -23514,27 +23869,26 @@ Save this key \u2014 it won't be shown again.`);
|
|
|
23514
23869
|
console.log("No credentials in this vault.");
|
|
23515
23870
|
return;
|
|
23516
23871
|
}
|
|
23517
|
-
table([["NAME", "TYPE", "DETAIL"], ...data.map((c3) => [c3.display_name, c3.auth.type, c3.auth.mcp_server_url || c3.auth.
|
|
23872
|
+
table([["NAME", "TYPE", "DETAIL"], ...data.map((c3) => [c3.display_name, c3.auth.type, c3.auth.mcp_server_url || c3.auth.cli_id || ""])]);
|
|
23518
23873
|
}
|
|
23519
23874
|
},
|
|
23520
23875
|
{
|
|
23521
23876
|
group: "Vaults",
|
|
23522
|
-
match: ["
|
|
23523
|
-
usage: "oma
|
|
23524
|
-
desc: "Add
|
|
23525
|
-
http: "POST /v1/vaults/:id/credentials {display_name, auth:{type,
|
|
23877
|
+
match: ["cli", "add"],
|
|
23878
|
+
usage: "oma cli add --vault <id> --name <n> --cli-id <gh|aws|...> --token <t>",
|
|
23879
|
+
desc: "Add cap CLI credential",
|
|
23880
|
+
http: "POST /v1/vaults/:id/credentials {display_name, auth:{type:cap_cli, cli_id, token}}",
|
|
23526
23881
|
async run(config2, args) {
|
|
23527
23882
|
const vaultId = flag(args, "--vault");
|
|
23528
23883
|
const name = flag(args, "--name");
|
|
23529
|
-
const
|
|
23530
|
-
const envVar = flag(args, "--env");
|
|
23884
|
+
const cliId = flag(args, "--cli-id");
|
|
23531
23885
|
const token = flag(args, "--token");
|
|
23532
|
-
if (!vaultId || !name || !
|
|
23533
|
-
console.error("Usage: oma
|
|
23886
|
+
if (!vaultId || !name || !cliId || !token) {
|
|
23887
|
+
console.error("Usage: oma cli add --vault <id> --name <name> --cli-id <gh|aws|kubectl|...> --token <token>");
|
|
23534
23888
|
process.exit(1);
|
|
23535
23889
|
}
|
|
23536
|
-
const cred = await apiFetch(config2, `/v1/vaults/${vaultId}/credentials`, { method: "POST", body: JSON.stringify({ display_name: name, auth: { type: "
|
|
23537
|
-
console.log(`
|
|
23890
|
+
const cred = await apiFetch(config2, `/v1/vaults/${vaultId}/credentials`, { method: "POST", body: JSON.stringify({ display_name: name, auth: { type: "cap_cli", cli_id: cliId, token } }) });
|
|
23891
|
+
console.log(`CLI credential added: ${name} (${cred.id})`);
|
|
23538
23892
|
}
|
|
23539
23893
|
},
|
|
23540
23894
|
// Skills
|
|
@@ -25050,6 +25404,7 @@ Usage:`);
|
|
|
25050
25404
|
Bridge (local runtime):
|
|
25051
25405
|
oma bridge setup [--force] [--no-service] Pair this machine with OMA + start daemon
|
|
25052
25406
|
oma bridge status Show creds + probe server reachability
|
|
25407
|
+
oma bridge refresh Reconcile authorized tenants + reload daemon
|
|
25053
25408
|
oma bridge agents refresh Re-detect agents + offer wrapper installs
|
|
25054
25409
|
oma bridge uninstall Stop service + remove creds
|
|
25055
25410
|
(oma bridge daemon Internal: launched by service mgr / debugging)
|
|
@@ -25136,9 +25491,25 @@ async function main() {
|
|
|
25136
25491
|
await runAgents2(args.slice(2));
|
|
25137
25492
|
return;
|
|
25138
25493
|
}
|
|
25494
|
+
case "refresh": {
|
|
25495
|
+
const { runRefresh: runRefresh2 } = await Promise.resolve().then(() => (init_refresh(), refresh_exports));
|
|
25496
|
+
await runRefresh2();
|
|
25497
|
+
return;
|
|
25498
|
+
}
|
|
25499
|
+
case "profiles": {
|
|
25500
|
+
const sub2 = args[2] ?? "list";
|
|
25501
|
+
if (sub2 !== "list" && sub2 !== "ls") {
|
|
25502
|
+
console.error(`Unknown subcommand: oma bridge profiles ${sub2}
|
|
25503
|
+
Try: oma bridge profiles list`);
|
|
25504
|
+
process.exit(1);
|
|
25505
|
+
}
|
|
25506
|
+
const { runProfilesList: runProfilesList2 } = await Promise.resolve().then(() => (init_profiles(), profiles_exports));
|
|
25507
|
+
await runProfilesList2();
|
|
25508
|
+
return;
|
|
25509
|
+
}
|
|
25139
25510
|
default:
|
|
25140
25511
|
console.error(
|
|
25141
|
-
"oma bridge \u2014 pair a local ACP agent with OMA\n\n oma bridge setup [--server-url=\u2026] [--no-service] [--force] [--yes]\n Pair + install service + start daemon\n oma bridge status Creds + service kind + probe server\n oma bridge agents refresh [--yes] Re-scan + offer-install wrappers + reload\n oma bridge uninstall Stop service + remove creds\n oma bridge daemon (internal \u2014 launched by service mgr / for debug)\n\n Use --profile <name> (or OMA_PROFILE=<name>) to run multiple\n daemons side-by-side (e.g. prod in launchd + staging foreground).\n"
|
|
25512
|
+
"oma bridge \u2014 pair a local ACP agent with OMA\n\n oma bridge setup [--server-url=\u2026] [--no-service] [--force] [--yes]\n Pair + install service + start daemon\n oma bridge status Creds + service kind + probe server\n oma bridge refresh Reconcile authorized tenants + reload daemon\n oma bridge profiles list List all installed daemon profiles on this machine\n oma bridge agents refresh [--yes] Re-scan + offer-install wrappers + reload\n oma bridge uninstall Stop service + remove creds\n oma bridge daemon (internal \u2014 launched by service mgr / for debug)\n\n Use --profile <name> (or OMA_PROFILE=<name>) to run multiple\n daemons side-by-side (e.g. prod in launchd + staging foreground).\n"
|
|
25142
25513
|
);
|
|
25143
25514
|
process.exit(sub ? 1 : 0);
|
|
25144
25515
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openma/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "CLI for the openma managed agents platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"cli",
|
|
25
25
|
"llm"
|
|
26
26
|
],
|
|
27
|
-
"license": "
|
|
27
|
+
"license": "Apache-2.0",
|
|
28
28
|
"homepage": "https://openma.dev",
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|