agent-factorio 0.3.4 → 0.4.1
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/commands/login.mjs +48 -28
- package/commands/org.mjs +2 -2
- package/lib/config.mjs +2 -2
- package/package.json +1 -1
package/commands/login.mjs
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* agent-factorio login —
|
|
2
|
+
* agent-factorio login — Browser-based authentication (like Claude Code /login)
|
|
3
3
|
*/
|
|
4
4
|
import { ask, choose } from "../lib/prompt.mjs";
|
|
5
5
|
import { readGlobalConfig, upsertOrg } from "../lib/config.mjs";
|
|
6
6
|
import { apiCall, checkHub } from "../lib/api.mjs";
|
|
7
7
|
import { success, error, info, dim } from "../lib/log.mjs";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Try to open a URL in the default browser
|
|
11
|
+
* @param {string} url
|
|
12
|
+
*/
|
|
13
|
+
async function openBrowser(url) {
|
|
14
|
+
const { exec } = await import("child_process");
|
|
15
|
+
const { platform } = await import("os");
|
|
16
|
+
|
|
17
|
+
const cmd =
|
|
18
|
+
platform() === "darwin"
|
|
19
|
+
? `open "${url}"`
|
|
20
|
+
: platform() === "win32"
|
|
21
|
+
? `start "" "${url}"`
|
|
22
|
+
: `xdg-open "${url}"`;
|
|
23
|
+
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
exec(cmd, (err) => resolve(!err));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
9
29
|
/**
|
|
10
30
|
* Poll verification status until verified or expired
|
|
11
31
|
* @param {string} hubUrl
|
|
@@ -23,7 +43,7 @@ async function waitForVerification(hubUrl, loginToken) {
|
|
|
23
43
|
});
|
|
24
44
|
|
|
25
45
|
if (res.status === 410) {
|
|
26
|
-
throw new Error("
|
|
46
|
+
throw new Error("Login session expired. Please try again.");
|
|
27
47
|
}
|
|
28
48
|
|
|
29
49
|
if (!res.ok) {
|
|
@@ -34,11 +54,10 @@ async function waitForVerification(hubUrl, loginToken) {
|
|
|
34
54
|
return { userId: res.data.userId, email: res.data.email };
|
|
35
55
|
}
|
|
36
56
|
|
|
37
|
-
// Wait before next poll
|
|
38
57
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
|
|
39
58
|
}
|
|
40
59
|
|
|
41
|
-
throw new Error("
|
|
60
|
+
throw new Error("Login timed out. Please try again.");
|
|
42
61
|
}
|
|
43
62
|
|
|
44
63
|
const DEFAULT_HUB_URL = "https://agent-factorio.vercel.app";
|
|
@@ -54,45 +73,46 @@ export async function loginCommand(options = {}) {
|
|
|
54
73
|
process.exit(1);
|
|
55
74
|
}
|
|
56
75
|
|
|
57
|
-
//
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
76
|
+
// 1. Initialize browser login session
|
|
77
|
+
const initRes = await apiCall(hubUrl, "/api/cli/login", {
|
|
78
|
+
body: { action: "init-browser-login" },
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (!initRes.ok) {
|
|
82
|
+
error(`Failed to initialize login: ${initRes.data?.error || "Unknown error"}`);
|
|
61
83
|
process.exit(1);
|
|
62
84
|
}
|
|
63
85
|
|
|
64
|
-
|
|
65
|
-
info("Sending verification email...");
|
|
66
|
-
const sendRes = await apiCall(hubUrl, "/api/cli/login", {
|
|
67
|
-
body: { action: "send-verification", email },
|
|
68
|
-
});
|
|
86
|
+
const { loginToken, loginUrl } = initRes.data;
|
|
69
87
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
// 2. Open browser
|
|
89
|
+
const opened = await openBrowser(loginUrl);
|
|
90
|
+
if (!opened) {
|
|
91
|
+
console.log();
|
|
92
|
+
info("Browser didn't open? Use the url below to sign in:");
|
|
93
|
+
console.log(` ${loginUrl}`);
|
|
73
94
|
}
|
|
74
95
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
info("Check your inbox and click the verification link.");
|
|
78
|
-
dim("Waiting for verification...");
|
|
96
|
+
console.log();
|
|
97
|
+
dim("Waiting for sign-in...");
|
|
79
98
|
|
|
80
|
-
//
|
|
81
|
-
let userId;
|
|
99
|
+
// 3. Poll for verification
|
|
100
|
+
let userId, email;
|
|
82
101
|
try {
|
|
83
102
|
const result = await waitForVerification(hubUrl, loginToken);
|
|
84
103
|
userId = result.userId;
|
|
104
|
+
email = result.email;
|
|
85
105
|
} catch (err) {
|
|
86
106
|
error(err.message);
|
|
87
107
|
process.exit(1);
|
|
88
108
|
}
|
|
89
109
|
|
|
90
|
-
success(
|
|
110
|
+
success(`Logged in as ${email}`);
|
|
91
111
|
|
|
92
|
-
//
|
|
112
|
+
// 4. Name input
|
|
93
113
|
const memberName = await ask("Your name (displayed in the org)", "CLI User");
|
|
94
114
|
|
|
95
|
-
//
|
|
115
|
+
// 5. Create or Join
|
|
96
116
|
const { index: actionIdx } = await choose("Create or join an organization?", [
|
|
97
117
|
"Create new",
|
|
98
118
|
"Join existing (invite code)",
|
|
@@ -116,7 +136,7 @@ export async function loginCommand(options = {}) {
|
|
|
116
136
|
}
|
|
117
137
|
|
|
118
138
|
const { orgId, orgName: name, inviteCode, memberId, authToken } = res.data;
|
|
119
|
-
upsertOrg({ hubUrl, orgId, orgName: name, inviteCode, memberName, email, memberId, userId, authToken });
|
|
139
|
+
upsertOrg({ hubUrl, orgId, orgName: name, inviteCode, memberName, email, memberId, userId, authToken }, { setAsDefault: true });
|
|
120
140
|
|
|
121
141
|
success(`Created "${name}" (${orgId})`);
|
|
122
142
|
info(`Invite code: ${inviteCode} — share with your team!`);
|
|
@@ -138,10 +158,10 @@ export async function loginCommand(options = {}) {
|
|
|
138
158
|
}
|
|
139
159
|
|
|
140
160
|
const { orgId, orgName, memberId, authToken } = res.data;
|
|
141
|
-
upsertOrg({ hubUrl, orgId, orgName, inviteCode: inviteCode.toUpperCase(), memberName, email, memberId, userId, authToken });
|
|
161
|
+
upsertOrg({ hubUrl, orgId, orgName, inviteCode: inviteCode.toUpperCase(), memberName, email, memberId, userId, authToken }, { setAsDefault: true });
|
|
142
162
|
|
|
143
163
|
success(`Joined "${orgName}" (${orgId})`);
|
|
144
164
|
}
|
|
145
165
|
|
|
146
|
-
console.log("\
|
|
166
|
+
console.log("\nLogin successful! Run `agent-factorio push` in any project to register an agent.");
|
|
147
167
|
}
|
package/commands/org.mjs
CHANGED
|
@@ -89,7 +89,7 @@ export async function orgCreateCommand(name) {
|
|
|
89
89
|
memberId,
|
|
90
90
|
userId: org.userId,
|
|
91
91
|
authToken,
|
|
92
|
-
});
|
|
92
|
+
}, { setAsDefault: true });
|
|
93
93
|
|
|
94
94
|
success(`Created "${createdName}" (${orgId})`);
|
|
95
95
|
info(`Invite code: ${inviteCode} — share with your team!`);
|
|
@@ -137,7 +137,7 @@ export async function orgJoinCommand(code) {
|
|
|
137
137
|
memberId,
|
|
138
138
|
userId: org.userId,
|
|
139
139
|
authToken,
|
|
140
|
-
});
|
|
140
|
+
}, { setAsDefault: true });
|
|
141
141
|
|
|
142
142
|
success(`Joined "${orgName}" (${orgId})`);
|
|
143
143
|
}
|
package/lib/config.mjs
CHANGED
|
@@ -60,7 +60,7 @@ export function getDefaultOrg() {
|
|
|
60
60
|
* Add or update an organization in global config
|
|
61
61
|
* @param {OrgEntry} org
|
|
62
62
|
*/
|
|
63
|
-
export function upsertOrg(org) {
|
|
63
|
+
export function upsertOrg(org, { setAsDefault = false } = {}) {
|
|
64
64
|
const config = readGlobalConfig() || { organizations: [] };
|
|
65
65
|
const idx = config.organizations.findIndex(
|
|
66
66
|
(o) => o.orgId === org.orgId && o.hubUrl === org.hubUrl
|
|
@@ -70,7 +70,7 @@ export function upsertOrg(org) {
|
|
|
70
70
|
} else {
|
|
71
71
|
config.organizations.push(org);
|
|
72
72
|
}
|
|
73
|
-
if (!config.defaultOrg) {
|
|
73
|
+
if (!config.defaultOrg || setAsDefault) {
|
|
74
74
|
config.defaultOrg = org.orgId;
|
|
75
75
|
}
|
|
76
76
|
writeGlobalConfig(config);
|