@runcofactor/cli 0.10.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/README.md +125 -0
- package/dist/api-catalog-GFGCLVS4.js +401 -0
- package/dist/api-catalog-GFGCLVS4.js.map +1 -0
- package/dist/attachment-SJC7D7FY.js +276 -0
- package/dist/attachment-SJC7D7FY.js.map +1 -0
- package/dist/auth-EN6NQCJY.js +31 -0
- package/dist/auth-EN6NQCJY.js.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-3CZU7XCS.js +272 -0
- package/dist/chunk-3CZU7XCS.js.map +1 -0
- package/dist/chunk-4NRDONSC.js +1048 -0
- package/dist/chunk-4NRDONSC.js.map +1 -0
- package/dist/chunk-4PNOV7OV.js +51980 -0
- package/dist/chunk-4PNOV7OV.js.map +1 -0
- package/dist/chunk-52IO7ACJ.js +2941 -0
- package/dist/chunk-52IO7ACJ.js.map +1 -0
- package/dist/chunk-6LEZCKKV.js +663 -0
- package/dist/chunk-6LEZCKKV.js.map +1 -0
- package/dist/chunk-A4OFTUVV.js +170 -0
- package/dist/chunk-A4OFTUVV.js.map +1 -0
- package/dist/chunk-B2VISN2V.js +2459 -0
- package/dist/chunk-B2VISN2V.js.map +1 -0
- package/dist/chunk-CLAWT7BJ.js +271 -0
- package/dist/chunk-CLAWT7BJ.js.map +1 -0
- package/dist/chunk-GKY2KXO5.js +281 -0
- package/dist/chunk-GKY2KXO5.js.map +1 -0
- package/dist/chunk-I4QLI3JO.js +413 -0
- package/dist/chunk-I4QLI3JO.js.map +1 -0
- package/dist/chunk-IMPZAX6L.js +180 -0
- package/dist/chunk-IMPZAX6L.js.map +1 -0
- package/dist/chunk-J6SH7GQT.js +241 -0
- package/dist/chunk-J6SH7GQT.js.map +1 -0
- package/dist/chunk-JDBCZ3Z2.js +298 -0
- package/dist/chunk-JDBCZ3Z2.js.map +1 -0
- package/dist/chunk-JICSYFRJ.js +46 -0
- package/dist/chunk-JICSYFRJ.js.map +1 -0
- package/dist/chunk-JWHYXEA2.js +32 -0
- package/dist/chunk-JWHYXEA2.js.map +1 -0
- package/dist/chunk-N7HFALX3.js +88 -0
- package/dist/chunk-N7HFALX3.js.map +1 -0
- package/dist/chunk-Q4BO4Z5V.js +178 -0
- package/dist/chunk-Q4BO4Z5V.js.map +1 -0
- package/dist/chunk-QFDOWABX.js +195 -0
- package/dist/chunk-QFDOWABX.js.map +1 -0
- package/dist/chunk-SJJW77WN.js +605 -0
- package/dist/chunk-SJJW77WN.js.map +1 -0
- package/dist/chunk-VKVL7WBN.js +50 -0
- package/dist/chunk-VKVL7WBN.js.map +1 -0
- package/dist/chunk-WOHL4FTB.js +353 -0
- package/dist/chunk-WOHL4FTB.js.map +1 -0
- package/dist/chunk-XQXVG7RA.js +426 -0
- package/dist/chunk-XQXVG7RA.js.map +1 -0
- package/dist/chunk-ZD3KHESI.js +94 -0
- package/dist/chunk-ZD3KHESI.js.map +1 -0
- package/dist/credential-COLTBV6D.js +450 -0
- package/dist/credential-COLTBV6D.js.map +1 -0
- package/dist/credential-template-EEPYFG7R.js +291 -0
- package/dist/credential-template-EEPYFG7R.js.map +1 -0
- package/dist/debug-workflow-KOVH5GOP.js +259 -0
- package/dist/debug-workflow-KOVH5GOP.js.map +1 -0
- package/dist/dist-44PZR7BQ.js +423 -0
- package/dist/dist-44PZR7BQ.js.map +1 -0
- package/dist/docs-3SSORHB5.js +366 -0
- package/dist/docs-3SSORHB5.js.map +1 -0
- package/dist/doctor-OTSIWV6G.js +132 -0
- package/dist/doctor-OTSIWV6G.js.map +1 -0
- package/dist/feature-flag-T2LRZME3.js +283 -0
- package/dist/feature-flag-T2LRZME3.js.map +1 -0
- package/dist/file-type-S3UUBVCT.js +4205 -0
- package/dist/file-type-S3UUBVCT.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +982 -0
- package/dist/index.js.map +1 -0
- package/dist/init-TGWRECT2.js +208 -0
- package/dist/init-TGWRECT2.js.map +1 -0
- package/dist/install-DXSNWZKO.js +295 -0
- package/dist/install-DXSNWZKO.js.map +1 -0
- package/dist/issues-XIZOPX6T.js +564 -0
- package/dist/issues-XIZOPX6T.js.map +1 -0
- package/dist/knobs-NBINZIUB.js +295 -0
- package/dist/knobs-NBINZIUB.js.map +1 -0
- package/dist/list-3TDHK637.js +59 -0
- package/dist/list-3TDHK637.js.map +1 -0
- package/dist/local-HICVD6PU.js +100 -0
- package/dist/local-HICVD6PU.js.map +1 -0
- package/dist/login-UOIUXVUK.js +315 -0
- package/dist/login-UOIUXVUK.js.map +1 -0
- package/dist/mcp-54IJEOUA.js +1956 -0
- package/dist/mcp-54IJEOUA.js.map +1 -0
- package/dist/pdf-extract-ABWV3L5X.js +13 -0
- package/dist/pdf-extract-ABWV3L5X.js.map +1 -0
- package/dist/pdfjs-DQ2YA5S3.js +41561 -0
- package/dist/pdfjs-DQ2YA5S3.js.map +1 -0
- package/dist/property-UXLOLPTD.js +701 -0
- package/dist/property-UXLOLPTD.js.map +1 -0
- package/dist/property-skill-guide.md +189 -0
- package/dist/psql-BYUUNLGG.js +115 -0
- package/dist/psql-BYUUNLGG.js.map +1 -0
- package/dist/record-4TR2UIIS.js +233 -0
- package/dist/record-4TR2UIIS.js.map +1 -0
- package/dist/redis-35VBUYW7.js +116 -0
- package/dist/redis-35VBUYW7.js.map +1 -0
- package/dist/release-OOLTUBUT.js +354 -0
- package/dist/release-OOLTUBUT.js.map +1 -0
- package/dist/sandbox-XGSUU6G7.js +2046 -0
- package/dist/sandbox-XGSUU6G7.js.map +1 -0
- package/dist/scenarios-GWLJEGHA.js +280 -0
- package/dist/scenarios-GWLJEGHA.js.map +1 -0
- package/dist/session-MYFNZPRP.js +2414 -0
- package/dist/session-MYFNZPRP.js.map +1 -0
- package/dist/skill-KM34LDMO.js +1839 -0
- package/dist/skill-KM34LDMO.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +262 -0
- package/dist/skills/debug-run/history/1.0.0.md +244 -0
- package/dist/skills/debug-run/history/1.0.1.md +245 -0
- package/dist/skills/debug-run/history/1.0.2.md +254 -0
- package/dist/skills/debug-run/history/1.1.0.md +262 -0
- package/dist/skills/debug-run/version.json +3 -0
- package/dist/spec-RA3CFEKG.js +951 -0
- package/dist/spec-RA3CFEKG.js.map +1 -0
- package/dist/src-LB4HBTJQ.js +1516 -0
- package/dist/src-LB4HBTJQ.js.map +1 -0
- package/dist/src-N3TPKGTY.js +244 -0
- package/dist/src-N3TPKGTY.js.map +1 -0
- package/dist/src-UVDX5UP6.js +17 -0
- package/dist/src-UVDX5UP6.js.map +1 -0
- package/dist/start-PSEQSQA3.js +151 -0
- package/dist/start-PSEQSQA3.js.map +1 -0
- package/dist/tag-YY6YAABQ.js +22 -0
- package/dist/tag-YY6YAABQ.js.map +1 -0
- package/dist/task-IQC3YR5K.js +922 -0
- package/dist/task-IQC3YR5K.js.map +1 -0
- package/dist/test-suite-DZNJFXDW.js +394 -0
- package/dist/test-suite-DZNJFXDW.js.map +1 -0
- package/dist/trace-HXIW7U2D.js +14 -0
- package/dist/trace-HXIW7U2D.js.map +1 -0
- package/dist/triage-ORG45R5W.js +16 -0
- package/dist/triage-ORG45R5W.js.map +1 -0
- package/dist/upgrade-L2VYMP2V.js +61 -0
- package/dist/upgrade-L2VYMP2V.js.map +1 -0
- package/dist/workflow-XYFH5LTP.js +3708 -0
- package/dist/workflow-XYFH5LTP.js.map +1 -0
- package/dist/workflow-cache-NQJQFWMV.js +253 -0
- package/dist/workflow-cache-NQJQFWMV.js.map +1 -0
- package/dist/workflow-debug-BGD4UIXU.js +927 -0
- package/dist/workflow-debug-BGD4UIXU.js.map +1 -0
- package/dist/workflow-proposals-MTUDX46O.js +382 -0
- package/dist/workflow-proposals-MTUDX46O.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
createSession,
|
|
4
|
+
deleteSession,
|
|
5
|
+
getSessionStorageState
|
|
6
|
+
} from "./chunk-J6SH7GQT.js";
|
|
7
|
+
import {
|
|
8
|
+
apiRequest,
|
|
9
|
+
fetchList,
|
|
10
|
+
promptChoice,
|
|
11
|
+
selectProperty,
|
|
12
|
+
uploadStorageState
|
|
13
|
+
} from "./chunk-WOHL4FTB.js";
|
|
14
|
+
import "./chunk-SJJW77WN.js";
|
|
15
|
+
import "./chunk-3CZU7XCS.js";
|
|
16
|
+
import {
|
|
17
|
+
getArgValue,
|
|
18
|
+
resolveConfig
|
|
19
|
+
} from "./chunk-A4OFTUVV.js";
|
|
20
|
+
import "./chunk-JWHYXEA2.js";
|
|
21
|
+
import "./chunk-VKVL7WBN.js";
|
|
22
|
+
|
|
23
|
+
// src/local/login.ts
|
|
24
|
+
import process from "process";
|
|
25
|
+
import { createParser } from "eventsource-parser";
|
|
26
|
+
async function runLocalLogin(argv) {
|
|
27
|
+
const config = await resolveConfig(argv);
|
|
28
|
+
const propertyHint = getArgValue(argv, "--property");
|
|
29
|
+
const envHint = getArgValue(argv, "--environment");
|
|
30
|
+
const credentialHint = getArgValue(argv, "--credential");
|
|
31
|
+
const modeFlag = getArgValue(argv, "--mode");
|
|
32
|
+
if (modeFlag && modeFlag !== "agent" && modeFlag !== "manual") {
|
|
33
|
+
console.error(`--mode must be one of: agent, manual (got "${modeFlag}")`);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const property = await selectProperty(config.apiUrl, config.token, propertyHint);
|
|
38
|
+
if (!property) {
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const envs = await fetchList(
|
|
43
|
+
config.apiUrl,
|
|
44
|
+
config.token,
|
|
45
|
+
`/org/properties/${property.id}/environments/local`,
|
|
46
|
+
"items"
|
|
47
|
+
);
|
|
48
|
+
if (envs.length === 0) {
|
|
49
|
+
console.error("No local environments found. Run `canary local init` first.");
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
let selectedEnv;
|
|
54
|
+
if (envHint) {
|
|
55
|
+
const match = envs.find(
|
|
56
|
+
(e) => e.id === envHint || e.name.toLowerCase() === envHint.toLowerCase()
|
|
57
|
+
);
|
|
58
|
+
if (!match) {
|
|
59
|
+
console.error(`Environment "${envHint}" not found.`);
|
|
60
|
+
console.error("Available environments:");
|
|
61
|
+
for (const e of envs) {
|
|
62
|
+
console.error(` - ${e.name} (${e.url})`);
|
|
63
|
+
}
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
selectedEnv = match;
|
|
68
|
+
} else if (envs.length === 1) {
|
|
69
|
+
selectedEnv = envs[0];
|
|
70
|
+
} else {
|
|
71
|
+
const picked = await promptChoice(
|
|
72
|
+
"Select a local environment:",
|
|
73
|
+
envs.map((e) => ({ label: `${e.name} \u2014 ${e.url}`, value: e }))
|
|
74
|
+
);
|
|
75
|
+
if (!picked) {
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
selectedEnv = picked;
|
|
80
|
+
}
|
|
81
|
+
const allCredentials = await fetchList(
|
|
82
|
+
config.apiUrl,
|
|
83
|
+
config.token,
|
|
84
|
+
"/org/credentials",
|
|
85
|
+
"items"
|
|
86
|
+
);
|
|
87
|
+
const envCredentials = allCredentials.filter(
|
|
88
|
+
(c) => c.propertyId === property.id
|
|
89
|
+
);
|
|
90
|
+
if (envCredentials.length === 0) {
|
|
91
|
+
console.error("No credentials found for this property.");
|
|
92
|
+
process.exitCode = 1;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
let credential;
|
|
96
|
+
if (credentialHint) {
|
|
97
|
+
const match = envCredentials.find(
|
|
98
|
+
(c) => c.id === credentialHint || c.name.toLowerCase() === credentialHint.toLowerCase()
|
|
99
|
+
);
|
|
100
|
+
if (!match) {
|
|
101
|
+
console.error(`Credential "${credentialHint}" not found.`);
|
|
102
|
+
console.error("Available credentials:");
|
|
103
|
+
for (const c of envCredentials) {
|
|
104
|
+
console.error(` - ${c.name}`);
|
|
105
|
+
}
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
credential = match;
|
|
110
|
+
} else if (envCredentials.length === 1) {
|
|
111
|
+
credential = envCredentials[0];
|
|
112
|
+
} else {
|
|
113
|
+
const picked = await promptChoice(
|
|
114
|
+
"Select a credential to refresh:",
|
|
115
|
+
envCredentials.map((c) => ({ label: c.name, value: c }))
|
|
116
|
+
);
|
|
117
|
+
if (!picked) {
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
credential = picked;
|
|
122
|
+
}
|
|
123
|
+
const detail = await fetchCredentialLoginInfo(
|
|
124
|
+
config.apiUrl,
|
|
125
|
+
config.token,
|
|
126
|
+
property.id,
|
|
127
|
+
credential.id
|
|
128
|
+
);
|
|
129
|
+
const canAgentLogin = Boolean(
|
|
130
|
+
detail?.loginInstructions && detail.data?.username && detail.data?.password && (detail.loginUrl || selectedEnv.url)
|
|
131
|
+
);
|
|
132
|
+
let mode;
|
|
133
|
+
if (modeFlag === "agent" || modeFlag === "manual") {
|
|
134
|
+
if (modeFlag === "agent" && !canAgentLogin) {
|
|
135
|
+
console.error(
|
|
136
|
+
"Credential is missing login instructions or stored username/password; cannot run agent login."
|
|
137
|
+
);
|
|
138
|
+
process.exitCode = 1;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
mode = modeFlag;
|
|
142
|
+
} else if (canAgentLogin) {
|
|
143
|
+
const choice = await promptChoice("How would you like to log in?", [
|
|
144
|
+
{ label: "Let the agent log in (no browser, fastest) \u2014 recommended", value: "agent" },
|
|
145
|
+
{ label: "Open a browser and I'll log in myself", value: "manual" }
|
|
146
|
+
]);
|
|
147
|
+
if (!choice) {
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
mode = choice;
|
|
152
|
+
} else {
|
|
153
|
+
if (!detail?.loginInstructions) {
|
|
154
|
+
console.log(
|
|
155
|
+
"\nTip: add login instructions to this credential so the agent can log in for you next time."
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
mode = "manual";
|
|
159
|
+
}
|
|
160
|
+
if (mode === "agent") {
|
|
161
|
+
const ok = await runAgentLogin({
|
|
162
|
+
apiUrl: config.apiUrl,
|
|
163
|
+
token: config.token,
|
|
164
|
+
propertyId: property.id,
|
|
165
|
+
credentialId: credential.id,
|
|
166
|
+
credentialName: credential.name
|
|
167
|
+
});
|
|
168
|
+
if (!ok) {
|
|
169
|
+
console.log("\nFalling back to manual browser login.");
|
|
170
|
+
await runManualLogin({
|
|
171
|
+
apiUrl: config.apiUrl,
|
|
172
|
+
token: config.token,
|
|
173
|
+
propertyId: property.id,
|
|
174
|
+
credentialId: credential.id,
|
|
175
|
+
url: selectedEnv.url
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
await runManualLogin({
|
|
181
|
+
apiUrl: config.apiUrl,
|
|
182
|
+
token: config.token,
|
|
183
|
+
propertyId: property.id,
|
|
184
|
+
credentialId: credential.id,
|
|
185
|
+
url: selectedEnv.url
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async function fetchCredentialLoginInfo(apiUrl, token, propertyId, credentialId) {
|
|
189
|
+
const result = await apiRequest(apiUrl, token, "GET", `/org/properties/${propertyId}/credentials/${credentialId}`);
|
|
190
|
+
if (!result.ok || !result.credential) return null;
|
|
191
|
+
return result.credential;
|
|
192
|
+
}
|
|
193
|
+
async function runAgentLogin(args) {
|
|
194
|
+
console.log(`
|
|
195
|
+
Asking the agent to log in as "${args.credentialName}"...`);
|
|
196
|
+
const res = await fetch(
|
|
197
|
+
`${args.apiUrl}/org/properties/${args.propertyId}/credentials/${args.credentialId}/mcp-test/stream`,
|
|
198
|
+
{
|
|
199
|
+
method: "POST",
|
|
200
|
+
headers: {
|
|
201
|
+
Authorization: `Bearer ${args.token}`,
|
|
202
|
+
Accept: "text/event-stream"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
if (!res.ok || !res.body) {
|
|
207
|
+
console.error(`Agent login could not start (HTTP ${res.status}).`);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
let success = null;
|
|
211
|
+
let errorMessage = null;
|
|
212
|
+
const parser = createParser({
|
|
213
|
+
onEvent: (event) => {
|
|
214
|
+
if (!event.data) return;
|
|
215
|
+
let data;
|
|
216
|
+
try {
|
|
217
|
+
data = JSON.parse(event.data);
|
|
218
|
+
} catch {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
switch (event.event) {
|
|
222
|
+
case "status": {
|
|
223
|
+
const { message } = data;
|
|
224
|
+
if (message) console.log(` ${message}`);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
case "log": {
|
|
228
|
+
const { level, message } = data;
|
|
229
|
+
if (message && level !== "debug") {
|
|
230
|
+
console.log(` ${message}`);
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case "complete": {
|
|
235
|
+
const summary = data;
|
|
236
|
+
success = summary.ok === true;
|
|
237
|
+
if (!success && summary.message) errorMessage = summary.message;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case "error": {
|
|
241
|
+
const { message } = data;
|
|
242
|
+
errorMessage = message ?? "Agent login failed";
|
|
243
|
+
success = false;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
const reader = res.body.getReader();
|
|
250
|
+
const decoder = new TextDecoder();
|
|
251
|
+
while (true) {
|
|
252
|
+
const { value, done } = await reader.read();
|
|
253
|
+
if (done) break;
|
|
254
|
+
parser.feed(decoder.decode(value, { stream: true }));
|
|
255
|
+
if (success !== null && !success && errorMessage) break;
|
|
256
|
+
}
|
|
257
|
+
if (success) {
|
|
258
|
+
console.log("\nLogin state refreshed by agent. Run `canary local start` to begin.");
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
console.error(`
|
|
262
|
+
Agent login failed: ${errorMessage ?? "unknown error"}`);
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
async function runManualLogin(args) {
|
|
266
|
+
console.log(`
|
|
267
|
+
Opening browser at ${args.url}...`);
|
|
268
|
+
const sessionResult = await createSession({
|
|
269
|
+
name: "local-login",
|
|
270
|
+
url: args.url
|
|
271
|
+
});
|
|
272
|
+
if (!sessionResult.ok || !sessionResult.data) {
|
|
273
|
+
console.error(`Failed to start browser: ${sessionResult.error ?? "Unknown error"}`);
|
|
274
|
+
process.exitCode = 1;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const sessionId = sessionResult.data.id;
|
|
278
|
+
console.log("Log in, then press Enter when done.");
|
|
279
|
+
const readline = await import("readline");
|
|
280
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
281
|
+
await new Promise((resolve) => {
|
|
282
|
+
rl.question("", () => {
|
|
283
|
+
rl.close();
|
|
284
|
+
resolve();
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
console.log("Extracting login state...");
|
|
288
|
+
const storageResult = await getSessionStorageState(sessionId);
|
|
289
|
+
if (!storageResult.ok || !storageResult.data) {
|
|
290
|
+
console.error(`Failed to extract storage state: ${storageResult.error ?? "Unknown error"}`);
|
|
291
|
+
await deleteSession(sessionId);
|
|
292
|
+
process.exitCode = 1;
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
console.log("Saving login state...");
|
|
296
|
+
const uploadResult = await uploadStorageState({
|
|
297
|
+
apiUrl: args.apiUrl,
|
|
298
|
+
token: args.token,
|
|
299
|
+
propertyId: args.propertyId,
|
|
300
|
+
credentialId: args.credentialId,
|
|
301
|
+
storageState: storageResult.data
|
|
302
|
+
});
|
|
303
|
+
if (!uploadResult.ok) {
|
|
304
|
+
console.error(`Failed to save login state: ${uploadResult.error ?? "Unknown error"}`);
|
|
305
|
+
await deleteSession(sessionId);
|
|
306
|
+
process.exitCode = 1;
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
await deleteSession(sessionId);
|
|
310
|
+
console.log("\nLogin state refreshed! Run `canary local start` to begin.");
|
|
311
|
+
}
|
|
312
|
+
export {
|
|
313
|
+
runLocalLogin
|
|
314
|
+
};
|
|
315
|
+
//# sourceMappingURL=login-UOIUXVUK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/local/login.ts"],"sourcesContent":["/**\n * `canary local login` — Re-login / refresh credentials for a local environment.\n *\n * @module\n */\n\nimport process from 'node:process';\nimport { createParser, type EventSourceMessage } from 'eventsource-parser';\nimport { getArgValue, resolveConfig } from '../auth.js';\nimport {\n selectProperty,\n promptChoice,\n fetchList,\n uploadStorageState,\n apiRequest,\n} from '../cli-helpers.js';\nimport {\n createSession,\n deleteSession,\n getSessionStorageState,\n} from '../session/daemon-client.js';\nimport type { CredentialListItem } from '../cli-helpers.js';\n\ninterface LocalEnvironment {\n id: string;\n name: string;\n url: string;\n propertyId: string;\n}\n\nexport async function runLocalLogin(argv: string[]) {\n const config = await resolveConfig(argv);\n const propertyHint = getArgValue(argv, '--property');\n const envHint = getArgValue(argv, '--environment');\n const credentialHint = getArgValue(argv, '--credential');\n const modeFlag = getArgValue(argv, '--mode');\n\n if (modeFlag && modeFlag !== 'agent' && modeFlag !== 'manual') {\n console.error(`--mode must be one of: agent, manual (got \"${modeFlag}\")`);\n process.exitCode = 1;\n return;\n }\n\n // Select property\n const property = await selectProperty(config.apiUrl, config.token, propertyHint);\n if (!property) {\n process.exitCode = 1;\n return;\n }\n\n // Get local environments\n const envs = await fetchList<LocalEnvironment>(\n config.apiUrl,\n config.token,\n `/org/properties/${property.id}/environments/local`,\n 'items'\n );\n\n if (envs.length === 0) {\n console.error('No local environments found. Run `canary local init` first.');\n process.exitCode = 1;\n return;\n }\n\n let selectedEnv: LocalEnvironment;\n if (envHint) {\n const match = envs.find(\n (e) => e.id === envHint || e.name.toLowerCase() === envHint.toLowerCase()\n );\n if (!match) {\n console.error(`Environment \"${envHint}\" not found.`);\n console.error('Available environments:');\n for (const e of envs) {\n console.error(` - ${e.name} (${e.url})`);\n }\n process.exitCode = 1;\n return;\n }\n selectedEnv = match;\n } else if (envs.length === 1) {\n selectedEnv = envs[0];\n } else {\n const picked = await promptChoice(\n 'Select a local environment:',\n envs.map((e) => ({ label: `${e.name} — ${e.url}`, value: e }))\n );\n if (!picked) {\n process.exitCode = 1;\n return;\n }\n selectedEnv = picked;\n }\n\n // Find credentials for this property\n const allCredentials = await fetchList<CredentialListItem>(\n config.apiUrl,\n config.token,\n '/org/credentials',\n 'items'\n );\n\n const envCredentials = allCredentials.filter(\n (c) => c.propertyId === property.id\n );\n\n if (envCredentials.length === 0) {\n console.error('No credentials found for this property.');\n process.exitCode = 1;\n return;\n }\n\n let credential: CredentialListItem;\n if (credentialHint) {\n const match = envCredentials.find(\n (c) => c.id === credentialHint || c.name.toLowerCase() === credentialHint.toLowerCase()\n );\n if (!match) {\n console.error(`Credential \"${credentialHint}\" not found.`);\n console.error('Available credentials:');\n for (const c of envCredentials) {\n console.error(` - ${c.name}`);\n }\n process.exitCode = 1;\n return;\n }\n credential = match;\n } else if (envCredentials.length === 1) {\n credential = envCredentials[0];\n } else {\n const picked = await promptChoice(\n 'Select a credential to refresh:',\n envCredentials.map((c) => ({ label: c.name, value: c }))\n );\n if (!picked) {\n process.exitCode = 1;\n return;\n }\n credential = picked;\n }\n\n // If the credential has login instructions + stored creds, offer the no-browser path first.\n const detail = await fetchCredentialLoginInfo(\n config.apiUrl,\n config.token,\n property.id,\n credential.id\n );\n const canAgentLogin = Boolean(\n detail?.loginInstructions &&\n detail.data?.username &&\n detail.data?.password &&\n (detail.loginUrl || selectedEnv.url)\n );\n\n let mode: 'agent' | 'manual';\n if (modeFlag === 'agent' || modeFlag === 'manual') {\n if (modeFlag === 'agent' && !canAgentLogin) {\n console.error(\n 'Credential is missing login instructions or stored username/password; cannot run agent login.'\n );\n process.exitCode = 1;\n return;\n }\n mode = modeFlag;\n } else if (canAgentLogin) {\n const choice = await promptChoice<'agent' | 'manual'>('How would you like to log in?', [\n { label: 'Let the agent log in (no browser, fastest) — recommended', value: 'agent' },\n { label: \"Open a browser and I'll log in myself\", value: 'manual' },\n ]);\n if (!choice) {\n process.exitCode = 1;\n return;\n }\n mode = choice;\n } else {\n if (!detail?.loginInstructions) {\n console.log(\n '\\nTip: add login instructions to this credential so the agent can log in for you next time.'\n );\n }\n mode = 'manual';\n }\n\n if (mode === 'agent') {\n const ok = await runAgentLogin({\n apiUrl: config.apiUrl,\n token: config.token,\n propertyId: property.id,\n credentialId: credential.id,\n credentialName: credential.name,\n });\n if (!ok) {\n console.log('\\nFalling back to manual browser login.');\n await runManualLogin({\n apiUrl: config.apiUrl,\n token: config.token,\n propertyId: property.id,\n credentialId: credential.id,\n url: selectedEnv.url,\n });\n }\n return;\n }\n\n await runManualLogin({\n apiUrl: config.apiUrl,\n token: config.token,\n propertyId: property.id,\n credentialId: credential.id,\n url: selectedEnv.url,\n });\n}\n\ntype CredentialLoginInfo = {\n id: string;\n name: string;\n authProvider?: string;\n loginUrl?: string | null;\n loginInstructions?: string | null;\n data?: { username?: string; password?: string } | null;\n};\n\nasync function fetchCredentialLoginInfo(\n apiUrl: string,\n token: string,\n propertyId: string,\n credentialId: string\n): Promise<CredentialLoginInfo | null> {\n const result = await apiRequest<{\n ok: boolean;\n credential?: CredentialLoginInfo;\n error?: string;\n }>(apiUrl, token, 'GET', `/org/properties/${propertyId}/credentials/${credentialId}`);\n if (!result.ok || !result.credential) return null;\n return result.credential;\n}\n\nasync function runAgentLogin(args: {\n apiUrl: string;\n token: string;\n propertyId: string;\n credentialId: string;\n credentialName: string;\n}): Promise<boolean> {\n console.log(`\\nAsking the agent to log in as \"${args.credentialName}\"...`);\n\n const res = await fetch(\n `${args.apiUrl}/org/properties/${args.propertyId}/credentials/${args.credentialId}/mcp-test/stream`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${args.token}`,\n Accept: 'text/event-stream',\n },\n }\n );\n\n if (!res.ok || !res.body) {\n console.error(`Agent login could not start (HTTP ${res.status}).`);\n return false;\n }\n\n let success: boolean | null = null;\n let errorMessage: string | null = null;\n\n const parser = createParser({\n onEvent: (event: EventSourceMessage) => {\n if (!event.data) return;\n let data: unknown;\n try {\n data = JSON.parse(event.data);\n } catch {\n return;\n }\n\n switch (event.event) {\n case 'status': {\n const { message } = data as { message?: string };\n if (message) console.log(` ${message}`);\n break;\n }\n case 'log': {\n const { level, message } = data as { level?: string; message?: string };\n if (message && level !== 'debug') {\n console.log(` ${message}`);\n }\n break;\n }\n case 'complete': {\n const summary = data as { ok?: boolean; message?: string | null };\n success = summary.ok === true;\n if (!success && summary.message) errorMessage = summary.message;\n break;\n }\n case 'error': {\n const { message } = data as { message?: string };\n errorMessage = message ?? 'Agent login failed';\n success = false;\n break;\n }\n }\n },\n });\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n parser.feed(decoder.decode(value, { stream: true }));\n if (success !== null && !success && errorMessage) break;\n }\n\n if (success) {\n console.log('\\nLogin state refreshed by agent. Run `canary local start` to begin.');\n return true;\n }\n\n console.error(`\\nAgent login failed: ${errorMessage ?? 'unknown error'}`);\n return false;\n}\n\nasync function runManualLogin(args: {\n apiUrl: string;\n token: string;\n propertyId: string;\n credentialId: string;\n url: string;\n}): Promise<void> {\n console.log(`\\nOpening browser at ${args.url}...`);\n const sessionResult = await createSession({\n name: 'local-login',\n url: args.url,\n });\n\n if (!sessionResult.ok || !sessionResult.data) {\n console.error(`Failed to start browser: ${sessionResult.error ?? 'Unknown error'}`);\n process.exitCode = 1;\n return;\n }\n\n const sessionId = sessionResult.data.id;\n\n console.log('Log in, then press Enter when done.');\n\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n await new Promise<void>((resolve) => {\n rl.question('', () => {\n rl.close();\n resolve();\n });\n });\n\n console.log('Extracting login state...');\n const storageResult = await getSessionStorageState(sessionId);\n\n if (!storageResult.ok || !storageResult.data) {\n console.error(`Failed to extract storage state: ${storageResult.error ?? 'Unknown error'}`);\n await deleteSession(sessionId);\n process.exitCode = 1;\n return;\n }\n\n console.log('Saving login state...');\n const uploadResult = await uploadStorageState({\n apiUrl: args.apiUrl,\n token: args.token,\n propertyId: args.propertyId,\n credentialId: args.credentialId,\n storageState: storageResult.data,\n });\n\n if (!uploadResult.ok) {\n console.error(`Failed to save login state: ${uploadResult.error ?? 'Unknown error'}`);\n await deleteSession(sessionId);\n process.exitCode = 1;\n return;\n }\n\n await deleteSession(sessionId);\n console.log('\\nLogin state refreshed! Run `canary local start` to begin.');\n}\n"],"mappings":"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","names":[]}
|