@glassly/cli 2.0.1 → 2.0.2-dev.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 CHANGED
@@ -61,4 +61,6 @@ Run `glassly <command> --help` for the full option set.
61
61
  The CLI talks to the production Glassly backend and Developer Console by
62
62
  default; nothing needs configuring. To point it at another deployment, set
63
63
  `CLOUD_CORE_URL`, `CLOUD_CONSOLE_URL`, and that backend's public WorkOS
64
- AuthKit client id in `WORKOS_CLIENT_ID`. Logins are stored per backend URL.
64
+ AuthKit client id in `WORKOS_CLIENT_ID`. Logins and the bundle signing key are
65
+ stored per backend URL in `~/.glassly/cli-v2/`, readable only by your user;
66
+ `glassly logout` removes the login.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassly/cli",
3
- "version": "2.0.1",
3
+ "version": "2.0.2-dev.0",
4
4
  "description": "The glassly command: develop, pack, release, and publish Glassly miniapps. Bun-only.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  "typecheck": "tsc --noEmit"
23
23
  },
24
24
  "dependencies": {
25
- "@glassly/miniapp-cli": "0.1.1",
25
+ "@glassly/miniapp-cli": "0.1.1-dev.0",
26
26
  "commander": "^14.0.2",
27
27
  "qrcode": "^1.5.4"
28
28
  },
@@ -36,23 +36,13 @@ export interface CliSigningKey {
36
36
  createdAt: string;
37
37
  }
38
38
 
39
+ // Credentials live in a 0600 file under ~/.glassly/cli-v2, like gh and npm
40
+ // keep theirs. The OS keychain (Bun.secrets) was the primary store before,
41
+ // but on some machines a write there succeeds and the next process reads
42
+ // nothing back, which showed up as "login works, whoami says sign in". The
43
+ // keychain is still read so sessions stored there keep working.
39
44
  export async function saveCredentials(credentials: CliCredentials): Promise<"keychain" | "file"> {
40
45
  const payload = JSON.stringify(credentials);
41
- const name = credentialName(credentials.coreUrl);
42
-
43
- try {
44
- if (typeof Bun !== "undefined" && Bun.secrets) {
45
- await Bun.secrets.set({
46
- service: SERVICE,
47
- name,
48
- value: payload,
49
- });
50
- return "keychain";
51
- }
52
- } catch {
53
- // Fall through to file storage.
54
- }
55
-
56
46
  mkdirSync(CREDS_DIR, { recursive: true });
57
47
  writeFileSync(credentialsFile(credentials.coreUrl), `${payload}\n`, { mode: 0o600 });
58
48
  return "file";
@@ -124,21 +114,6 @@ export async function clearCredentials(coreUrl?: string): Promise<void> {
124
114
 
125
115
  export async function saveSigningKey(key: CliSigningKey): Promise<"keychain" | "file"> {
126
116
  const payload = JSON.stringify(key);
127
- const name = credentialName(key.coreUrl);
128
-
129
- try {
130
- if (typeof Bun !== "undefined" && Bun.secrets) {
131
- await Bun.secrets.set({
132
- service: SIGNING_KEY_SERVICE,
133
- name,
134
- value: payload,
135
- });
136
- return "keychain";
137
- }
138
- } catch {
139
- // Fall through to file storage.
140
- }
141
-
142
117
  mkdirSync(CREDS_DIR, { recursive: true });
143
118
  writeFileSync(signingKeyFile(key.coreUrl), `${payload}\n`, { mode: 0o600 });
144
119
  return "file";
@@ -146,6 +121,14 @@ export async function saveSigningKey(key: CliSigningKey): Promise<"keychain" | "
146
121
 
147
122
  export async function loadSigningKey(coreUrl: string): Promise<CliSigningKey | null> {
148
123
  const targetCoreUrl = normalizeCoreUrl(coreUrl);
124
+ try {
125
+ const path = signingKeyFile(targetCoreUrl);
126
+ if (existsSync(path)) return JSON.parse(readFileSync(path, "utf8")) as CliSigningKey;
127
+ } catch {
128
+ // Treat corrupt key storage as missing.
129
+ }
130
+
131
+ // Keys created by earlier versions sit in the OS keychain.
149
132
  try {
150
133
  if (typeof Bun !== "undefined" && Bun.secrets) {
151
134
  const value = await Bun.secrets.get({
@@ -158,17 +141,18 @@ export async function loadSigningKey(coreUrl: string): Promise<CliSigningKey | n
158
141
  // Fall through.
159
142
  }
160
143
 
161
- try {
162
- const path = signingKeyFile(targetCoreUrl);
163
- if (existsSync(path)) return JSON.parse(readFileSync(path, "utf8")) as CliSigningKey;
164
- } catch {
165
- // Treat corrupt key storage as missing.
166
- }
167
-
168
144
  return null;
169
145
  }
170
146
 
171
147
  async function loadScopedCredentials(coreUrl: string): Promise<CliCredentials | null> {
148
+ try {
149
+ const path = credentialsFile(coreUrl);
150
+ if (existsSync(path)) return JSON.parse(readFileSync(path, "utf8")) as CliCredentials;
151
+ } catch {
152
+ // Treat corrupt credentials as logged out.
153
+ }
154
+
155
+ // Sessions stored by earlier versions sit in the OS keychain.
172
156
  try {
173
157
  if (typeof Bun !== "undefined" && Bun.secrets) {
174
158
  const value = await Bun.secrets.get({
@@ -181,13 +165,6 @@ async function loadScopedCredentials(coreUrl: string): Promise<CliCredentials |
181
165
  // Fall through.
182
166
  }
183
167
 
184
- try {
185
- const path = credentialsFile(coreUrl);
186
- if (existsSync(path)) return JSON.parse(readFileSync(path, "utf8")) as CliCredentials;
187
- } catch {
188
- // Treat corrupt credentials as logged out.
189
- }
190
-
191
168
  return null;
192
169
  }
193
170
 
package/src/index.ts CHANGED
@@ -135,7 +135,7 @@ program
135
135
  });
136
136
  console.log(`Signed in as ${token.user.email}`);
137
137
  if (token.organization_id) console.log(`Organization: ${token.organization_id}`);
138
- console.log(`Credentials stored in ${storage === "keychain" ? "OS keychain" : "~/.glassly/cli-v2"}`);
138
+ console.log(`Credentials stored in ${storage === "keychain" ? "the OS keychain" : "~/.glassly/cli-v2 (readable only by you)"}`);
139
139
  return;
140
140
  }
141
141