@openma/cli 0.4.1 → 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 +392 -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
|
|
|
@@ -7172,14 +7220,22 @@ ${c.bold("Up to date.")}
|
|
|
7172
7220
|
machine_id: machineId,
|
|
7173
7221
|
hostname: hostname(),
|
|
7174
7222
|
os: osTag(),
|
|
7175
|
-
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
|
|
7176
7227
|
});
|
|
7177
|
-
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)`);
|
|
7178
7229
|
await writeCreds({
|
|
7230
|
+
v: 2,
|
|
7179
7231
|
serverUrl: opts.serverUrl,
|
|
7180
7232
|
runtimeId: exchange.runtime_id,
|
|
7181
7233
|
token: exchange.token,
|
|
7182
|
-
|
|
7234
|
+
tenants: exchange.tenants.map((t) => ({
|
|
7235
|
+
id: t.id,
|
|
7236
|
+
name: t.name,
|
|
7237
|
+
agentApiKey: t.agent_api_key
|
|
7238
|
+
})),
|
|
7183
7239
|
machineId,
|
|
7184
7240
|
createdAt: Math.floor(Date.now() / 1e3)
|
|
7185
7241
|
});
|
|
@@ -7265,8 +7321,12 @@ async function installAndReport(opts) {
|
|
|
7265
7321
|
}
|
|
7266
7322
|
process.stderr.write("\n");
|
|
7267
7323
|
process.stderr.write(`${c.bold("Done.")} the runtime should appear online at ${c.cyan(opts.browserOrigin)}
|
|
7268
|
-
|
|
7269
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
|
+
);
|
|
7270
7330
|
}
|
|
7271
7331
|
function execIntoDaemon() {
|
|
7272
7332
|
const profile = currentProfile();
|
|
@@ -21668,7 +21728,14 @@ var init_session_manager = __esm({
|
|
|
21668
21728
|
#spawner = new NodeSpawner();
|
|
21669
21729
|
#runtime = new AcpRuntimeImpl(this.#spawner);
|
|
21670
21730
|
#sessions = /* @__PURE__ */ new Map();
|
|
21671
|
-
#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();
|
|
21672
21739
|
/** Set by `drain()` to refuse new session.start while in-flight turns
|
|
21673
21740
|
* finish. Existing sessions keep accepting prompts so a user mid-turn
|
|
21674
21741
|
* doesn't hit "session not ready" mid-stream just because the daemon
|
|
@@ -21680,6 +21747,21 @@ var init_session_manager = __esm({
|
|
|
21680
21747
|
setSpawnEnv(env) {
|
|
21681
21748
|
this.#env = env;
|
|
21682
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
|
+
}
|
|
21683
21765
|
setSender(send) {
|
|
21684
21766
|
this.#send = send;
|
|
21685
21767
|
}
|
|
@@ -21701,7 +21783,7 @@ var init_session_manager = __esm({
|
|
|
21701
21783
|
/** Re-announce alive sessions to the server (used after WS reconnect). */
|
|
21702
21784
|
announceAll() {
|
|
21703
21785
|
for (const [session_id, sess] of this.#sessions) {
|
|
21704
|
-
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 });
|
|
21705
21787
|
}
|
|
21706
21788
|
}
|
|
21707
21789
|
async start(p) {
|
|
@@ -21714,11 +21796,38 @@ var init_session_manager = __esm({
|
|
|
21714
21796
|
});
|
|
21715
21797
|
return;
|
|
21716
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
|
+
);
|
|
21717
21825
|
const existing = this.#sessions.get(p.session_id);
|
|
21718
21826
|
if (existing) {
|
|
21719
21827
|
this.#send({
|
|
21720
21828
|
type: "session.ready",
|
|
21721
21829
|
session_id: p.session_id,
|
|
21830
|
+
tenant_id: existing.tenantId,
|
|
21722
21831
|
acp_session_id: existing.acpSessionId
|
|
21723
21832
|
});
|
|
21724
21833
|
return;
|
|
@@ -21786,7 +21895,7 @@ var init_session_manager = __esm({
|
|
|
21786
21895
|
type: s.type,
|
|
21787
21896
|
name: s.name,
|
|
21788
21897
|
url: s.url,
|
|
21789
|
-
headers:
|
|
21898
|
+
headers: tenantKey ? [{ name: "Authorization", value: `Bearer ${tenantKey}` }] : []
|
|
21790
21899
|
}));
|
|
21791
21900
|
process.stderr.write(
|
|
21792
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
|
|
@@ -21807,17 +21916,20 @@ var init_session_manager = __esm({
|
|
|
21807
21916
|
this.#sessions.set(p.session_id, {
|
|
21808
21917
|
acp: session,
|
|
21809
21918
|
acpSessionId: session.acpSessionId,
|
|
21919
|
+
tenantId,
|
|
21810
21920
|
turns: /* @__PURE__ */ new Map()
|
|
21811
21921
|
});
|
|
21812
21922
|
this.#send({
|
|
21813
21923
|
type: "session.ready",
|
|
21814
21924
|
session_id: p.session_id,
|
|
21925
|
+
tenant_id: tenantId,
|
|
21815
21926
|
acp_session_id: session.acpSessionId
|
|
21816
21927
|
});
|
|
21817
21928
|
} catch (e) {
|
|
21818
21929
|
this.#send({
|
|
21819
21930
|
type: "session.error",
|
|
21820
21931
|
session_id: p.session_id,
|
|
21932
|
+
tenant_id: tenantId,
|
|
21821
21933
|
message: e instanceof Error ? e.message : String(e)
|
|
21822
21934
|
});
|
|
21823
21935
|
}
|
|
@@ -21828,6 +21940,7 @@ var init_session_manager = __esm({
|
|
|
21828
21940
|
this.#send({
|
|
21829
21941
|
type: "session.error",
|
|
21830
21942
|
session_id: p.session_id,
|
|
21943
|
+
tenant_id: p.tenant_id,
|
|
21831
21944
|
turn_id: p.turn_id,
|
|
21832
21945
|
message: "no such session"
|
|
21833
21946
|
});
|
|
@@ -21848,6 +21961,7 @@ var init_session_manager = __esm({
|
|
|
21848
21961
|
this.#send({
|
|
21849
21962
|
type: "session.event",
|
|
21850
21963
|
session_id: p.session_id,
|
|
21964
|
+
tenant_id: sess.tenantId,
|
|
21851
21965
|
turn_id: p.turn_id,
|
|
21852
21966
|
event: ev
|
|
21853
21967
|
});
|
|
@@ -21856,16 +21970,18 @@ var init_session_manager = __esm({
|
|
|
21856
21970
|
this.#send({
|
|
21857
21971
|
type: "session.error",
|
|
21858
21972
|
session_id: p.session_id,
|
|
21973
|
+
tenant_id: sess.tenantId,
|
|
21859
21974
|
turn_id: p.turn_id,
|
|
21860
21975
|
message: promptErr
|
|
21861
21976
|
});
|
|
21862
21977
|
} else {
|
|
21863
|
-
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 });
|
|
21864
21979
|
}
|
|
21865
21980
|
} catch (e) {
|
|
21866
21981
|
this.#send({
|
|
21867
21982
|
type: "session.error",
|
|
21868
21983
|
session_id: p.session_id,
|
|
21984
|
+
tenant_id: sess.tenantId,
|
|
21869
21985
|
turn_id: p.turn_id,
|
|
21870
21986
|
message: e instanceof Error ? e.message : String(e)
|
|
21871
21987
|
});
|
|
@@ -21879,9 +21995,11 @@ var init_session_manager = __esm({
|
|
|
21879
21995
|
sess.turns.get(turn_id)?.abort();
|
|
21880
21996
|
}
|
|
21881
21997
|
async dispose(session_id) {
|
|
21998
|
+
const sess = this.#sessions.get(session_id);
|
|
21999
|
+
const tenantId = sess?.tenantId ?? "";
|
|
21882
22000
|
await this.#killChild(session_id);
|
|
21883
22001
|
await removeSessionCwd(session_id);
|
|
21884
|
-
this.#send({ type: "session.disposed", session_id });
|
|
22002
|
+
this.#send({ type: "session.disposed", session_id, tenant_id: tenantId });
|
|
21885
22003
|
}
|
|
21886
22004
|
/** Best-effort cleanup on daemon shutdown. KEEPS spawn cwds — sessions are
|
|
21887
22005
|
* still live at the platform; the daemon coming back tomorrow needs the
|
|
@@ -22245,8 +22363,15 @@ async function runDaemon() {
|
|
|
22245
22363
|
}
|
|
22246
22364
|
process.on("SIGHUP", () => {
|
|
22247
22365
|
void (async () => {
|
|
22248
|
-
log.step("SIGHUP \u2014 refreshing agent detection");
|
|
22366
|
+
log.step("SIGHUP \u2014 refreshing agent detection + credentials");
|
|
22249
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
|
+
}
|
|
22250
22375
|
await loadRegistry({ cachePath, forceRefresh: true });
|
|
22251
22376
|
const agents = (await detectAll()).map((a) => ({ id: a.id, binary: a.spec.command }));
|
|
22252
22377
|
const localSkillsDetailed = await detectLocalSkills();
|
|
@@ -22278,10 +22403,10 @@ async function runDaemon() {
|
|
|
22278
22403
|
const sessions = new SessionManager(() => {
|
|
22279
22404
|
});
|
|
22280
22405
|
sessions.setSpawnEnv({
|
|
22281
|
-
apiKey: creds.agentApiKey ?? "",
|
|
22282
22406
|
apiUrl: creds.serverUrl,
|
|
22283
22407
|
runtimeToken: creds.token
|
|
22284
22408
|
});
|
|
22409
|
+
sessions.setTenantKeys(creds.tenants);
|
|
22285
22410
|
while (!stopping) {
|
|
22286
22411
|
try {
|
|
22287
22412
|
const ws = new wrapper_default(wsUrl, {
|
|
@@ -22596,22 +22721,221 @@ var init_agents = __esm({
|
|
|
22596
22721
|
}
|
|
22597
22722
|
});
|
|
22598
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
|
+
|
|
22599
22923
|
// src/index.ts
|
|
22600
22924
|
init_platform();
|
|
22601
22925
|
import { createServer as createServer2 } from "node:http";
|
|
22602
22926
|
import { execSync } from "node:child_process";
|
|
22603
|
-
import { homedir as
|
|
22604
|
-
import { join as
|
|
22605
|
-
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";
|
|
22606
22930
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
22607
22931
|
function credentialsPath() {
|
|
22608
22932
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
22609
|
-
const base = xdg && xdg.length > 0 ? xdg :
|
|
22933
|
+
const base = xdg && xdg.length > 0 ? xdg : join12(homedir6(), ".config");
|
|
22610
22934
|
const profile = currentProfile();
|
|
22611
22935
|
const file2 = profile ? `credentials.${profile}.json` : "credentials.json";
|
|
22612
|
-
return
|
|
22936
|
+
return join12(base, "oma", file2);
|
|
22613
22937
|
}
|
|
22614
|
-
function
|
|
22938
|
+
function migrateV1ToV22(v1) {
|
|
22615
22939
|
return {
|
|
22616
22940
|
version: 2,
|
|
22617
22941
|
base_url: v1.base_url,
|
|
@@ -22632,11 +22956,11 @@ function readCredentials() {
|
|
|
22632
22956
|
const path2 = credentialsPath();
|
|
22633
22957
|
try {
|
|
22634
22958
|
if (!existsSync2(path2)) return null;
|
|
22635
|
-
const raw =
|
|
22959
|
+
const raw = readFileSync3(path2, "utf8");
|
|
22636
22960
|
const parsed = JSON.parse(raw);
|
|
22637
22961
|
if (parsed.version === 2) return parsed;
|
|
22638
22962
|
if (parsed.version === 1) {
|
|
22639
|
-
const v2 =
|
|
22963
|
+
const v2 = migrateV1ToV22(parsed);
|
|
22640
22964
|
try {
|
|
22641
22965
|
writeCredentials(v2);
|
|
22642
22966
|
} catch {
|
|
@@ -22853,9 +23177,11 @@ async function streamChat(config2, sessionId, text) {
|
|
|
22853
23177
|
}
|
|
22854
23178
|
}
|
|
22855
23179
|
async function tailSession(config2, sessionId) {
|
|
22856
|
-
const res = await rawStream(
|
|
22857
|
-
|
|
22858
|
-
|
|
23180
|
+
const res = await rawStream(
|
|
23181
|
+
config2,
|
|
23182
|
+
`/v1/sessions/${sessionId}/events/stream?include=chunks&replay=1`,
|
|
23183
|
+
{ headers: { accept: "text/event-stream" } }
|
|
23184
|
+
);
|
|
22859
23185
|
for await (const ev of parseSSE(res)) {
|
|
22860
23186
|
process.stdout.write(JSON.stringify(ev) + "\n");
|
|
22861
23187
|
}
|
|
@@ -23204,7 +23530,7 @@ var commands = [
|
|
|
23204
23530
|
match: ["agents", "create"],
|
|
23205
23531
|
usage: "oma agents create <name> [--model <id>]",
|
|
23206
23532
|
desc: "Create agent",
|
|
23207
|
-
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,...}}",
|
|
23208
23534
|
async run(config2, args) {
|
|
23209
23535
|
const name = flag(args, "--name") || args.find((a) => !a.startsWith("--"));
|
|
23210
23536
|
const model = flag(args, "--model") || "claude-sonnet-4-6";
|
|
@@ -23223,8 +23549,10 @@ var commands = [
|
|
|
23223
23549
|
tools: [{ type: "agent_toolset_20260401" }]
|
|
23224
23550
|
};
|
|
23225
23551
|
if (useAcpProxy) {
|
|
23226
|
-
body.
|
|
23227
|
-
|
|
23552
|
+
body._oma = {
|
|
23553
|
+
harness: "acp-proxy",
|
|
23554
|
+
runtime_binding: { runtime_id: runtimeId, acp_agent_id: acpAgentId }
|
|
23555
|
+
};
|
|
23228
23556
|
}
|
|
23229
23557
|
const agent = await apiFetch(config2, "/v1/agents", { method: "POST", body: JSON.stringify(body) });
|
|
23230
23558
|
console.log(`Agent created: ${agent.name} (${agent.id})${useAcpProxy ? ` [acp-proxy \u2192 ${acpAgentId} on ${runtimeId.slice(0, 8)}\u2026]` : ""}`);
|
|
@@ -23434,27 +23762,27 @@ Version: v${a.version}`);
|
|
|
23434
23762
|
console.log("No model cards. Create one with: oma models create");
|
|
23435
23763
|
return;
|
|
23436
23764
|
}
|
|
23437
|
-
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" : ""])]);
|
|
23438
23766
|
}
|
|
23439
23767
|
},
|
|
23440
23768
|
{
|
|
23441
23769
|
group: "Model Cards",
|
|
23442
23770
|
match: ["models", "create"],
|
|
23443
|
-
usage: "oma models create --
|
|
23771
|
+
usage: "oma models create --model-id <id> --api-key <key> [--model <wire>] [--provider <p>]",
|
|
23444
23772
|
desc: "Create model card",
|
|
23445
|
-
http: "POST /v1/model_cards {
|
|
23773
|
+
http: "POST /v1/model_cards {model_id, provider, model?, api_key, base_url?, is_default?}",
|
|
23446
23774
|
async run(config2, args) {
|
|
23447
|
-
const name = flag(args, "--name");
|
|
23448
|
-
const provider = flag(args, "--provider") || "ant";
|
|
23449
23775
|
const modelId = flag(args, "--model-id");
|
|
23776
|
+
const provider = flag(args, "--provider") || "ant";
|
|
23777
|
+
const model = flag(args, "--model");
|
|
23450
23778
|
const apiKey = flag(args, "--api-key");
|
|
23451
23779
|
const baseUrl = flag(args, "--base-url");
|
|
23452
|
-
if (!
|
|
23453
|
-
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>]");
|
|
23454
23782
|
process.exit(1);
|
|
23455
23783
|
}
|
|
23456
|
-
const card = await apiFetch(config2, "/v1/model_cards", { method: "POST", body: JSON.stringify({
|
|
23457
|
-
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})`);
|
|
23458
23786
|
}
|
|
23459
23787
|
},
|
|
23460
23788
|
// API Keys
|
|
@@ -23541,27 +23869,26 @@ Save this key \u2014 it won't be shown again.`);
|
|
|
23541
23869
|
console.log("No credentials in this vault.");
|
|
23542
23870
|
return;
|
|
23543
23871
|
}
|
|
23544
|
-
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 || ""])]);
|
|
23545
23873
|
}
|
|
23546
23874
|
},
|
|
23547
23875
|
{
|
|
23548
23876
|
group: "Vaults",
|
|
23549
|
-
match: ["
|
|
23550
|
-
usage: "oma
|
|
23551
|
-
desc: "Add
|
|
23552
|
-
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}}",
|
|
23553
23881
|
async run(config2, args) {
|
|
23554
23882
|
const vaultId = flag(args, "--vault");
|
|
23555
23883
|
const name = flag(args, "--name");
|
|
23556
|
-
const
|
|
23557
|
-
const envVar = flag(args, "--env");
|
|
23884
|
+
const cliId = flag(args, "--cli-id");
|
|
23558
23885
|
const token = flag(args, "--token");
|
|
23559
|
-
if (!vaultId || !name || !
|
|
23560
|
-
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>");
|
|
23561
23888
|
process.exit(1);
|
|
23562
23889
|
}
|
|
23563
|
-
const cred = await apiFetch(config2, `/v1/vaults/${vaultId}/credentials`, { method: "POST", body: JSON.stringify({ display_name: name, auth: { type: "
|
|
23564
|
-
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})`);
|
|
23565
23892
|
}
|
|
23566
23893
|
},
|
|
23567
23894
|
// Skills
|
|
@@ -25077,6 +25404,7 @@ Usage:`);
|
|
|
25077
25404
|
Bridge (local runtime):
|
|
25078
25405
|
oma bridge setup [--force] [--no-service] Pair this machine with OMA + start daemon
|
|
25079
25406
|
oma bridge status Show creds + probe server reachability
|
|
25407
|
+
oma bridge refresh Reconcile authorized tenants + reload daemon
|
|
25080
25408
|
oma bridge agents refresh Re-detect agents + offer wrapper installs
|
|
25081
25409
|
oma bridge uninstall Stop service + remove creds
|
|
25082
25410
|
(oma bridge daemon Internal: launched by service mgr / debugging)
|
|
@@ -25163,9 +25491,25 @@ async function main() {
|
|
|
25163
25491
|
await runAgents2(args.slice(2));
|
|
25164
25492
|
return;
|
|
25165
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
|
+
}
|
|
25166
25510
|
default:
|
|
25167
25511
|
console.error(
|
|
25168
|
-
"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"
|
|
25169
25513
|
);
|
|
25170
25514
|
process.exit(sub ? 1 : 0);
|
|
25171
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",
|