latticesql 1.13.4 → 1.13.5

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/dist/cli.js CHANGED
@@ -7613,8 +7613,10 @@ var guiAppHtml = `<!doctype html>
7613
7613
  // enters per-team things (cloud URL + team name) in this modal.
7614
7614
  fetchJson('/api/userconfig/identity').then(function (id) {
7615
7615
  var bodyHtml =
7616
- '<div class="field"><label>Cloud URL</label><input name="cloud_url" placeholder="http://localhost:4317" /></div>' +
7617
- '<div class="field"><label>Your email</label><input name="email" value="' + escapeHtml(id.email || '') + '" /></div>' +
7616
+ '<div class="field"><label>Cloud URL</label>' +
7617
+ '<input name="cloud_url" placeholder="postgres://postgres.&lt;ref&gt;:password@aws-x-region.pooler.supabase.com:5432/postgres" autocapitalize="off" autocorrect="off" spellcheck="false" />' +
7618
+ '</div>' +
7619
+ '<div class="field"><label>Your email</label><input name="email" value="' + escapeHtml(id.email || '') + '" autocapitalize="off" /></div>' +
7618
7620
  '<div class="field"><label>Your display name</label><input name="user_name" value="' + escapeHtml(id.display_name || '') + '" /></div>' +
7619
7621
  '<div class="field"><label>Team name</label><input name="team_name" /></div>' +
7620
7622
  '<p style="font-size:12px;color:var(--text-muted);margin:0">' +
@@ -7638,12 +7640,14 @@ var guiAppHtml = `<!doctype html>
7638
7640
  function showJoinTeamModal(kind) {
7639
7641
  fetchJson('/api/userconfig/identity').then(function (id) {
7640
7642
  var bodyHtml =
7641
- '<div class="field"><label>Cloud URL</label><input name="cloud_url" placeholder="http://localhost:4317" /></div>' +
7642
- '<div class="field"><label>Invite token</label><textarea name="invite_token" placeholder="latinv_..."></textarea></div>' +
7643
- '<div class="field"><label>Your email</label><input name="email" value="' + escapeHtml(id.email || '') + '" /></div>' +
7643
+ '<div class="field"><label>Cloud URL</label>' +
7644
+ '<input name="cloud_url" placeholder="postgres://postgres.&lt;ref&gt;:password@aws-x-region.pooler.supabase.com:5432/postgres" autocapitalize="off" autocorrect="off" spellcheck="false" />' +
7645
+ '</div>' +
7646
+ '<div class="field"><label>Invite token</label><textarea name="invite_token" placeholder="latinv_..." autocapitalize="off" autocorrect="off" spellcheck="false"></textarea></div>' +
7647
+ '<div class="field"><label>Your email</label><input name="email" value="' + escapeHtml(id.email || '') + '" autocapitalize="off" /></div>' +
7644
7648
  '<div class="field"><label>Your display name</label><input name="name" value="' + escapeHtml(id.display_name || '') + '" /></div>' +
7645
7649
  '<p style="font-size:12px;color:var(--text-muted);margin:0">' +
7646
- 'Email must match the address the invitation was addressed to.' +
7650
+ 'Use the same Postgres URL the inviter used (postgres://\u2026). Email must match the address the invitation was addressed to.' +
7647
7651
  '</p>';
7648
7652
  showModal('Join team', bodyHtml, {
7649
7653
  primaryLabel: 'Join',
@@ -10117,6 +10121,75 @@ async function destroyTeamDirect(db) {
10117
10121
  }
10118
10122
  await db.delete("__lattice_team_identity", "singleton");
10119
10123
  }
10124
+ async function redeemInviteDirect(cloudUrl, inviteToken, email, name) {
10125
+ if (!isPostgresUrl(cloudUrl)) {
10126
+ throw new Error(
10127
+ `redeemInviteDirect: cloudUrl must be a postgres:// URL (got ${cloudUrl.slice(0, 12)}\u2026)`
10128
+ );
10129
+ }
10130
+ const db = new Lattice(cloudUrl);
10131
+ try {
10132
+ await db.init();
10133
+ for (const [table, def] of Object.entries(CLOUD_INTERNAL_TABLE_DEFS)) {
10134
+ await db.defineLate(table, def);
10135
+ }
10136
+ const invites = await db.query("__lattice_invitations", {
10137
+ filters: [
10138
+ { col: "token_hash", op: "eq", val: hashToken(inviteToken) },
10139
+ { col: "redeemed_at", op: "isNull" }
10140
+ ],
10141
+ limit: 1
10142
+ });
10143
+ const invite = invites[0];
10144
+ if (!invite) {
10145
+ throw new Error("Invitation invalid or already used");
10146
+ }
10147
+ if (invite.expires_at && new Date(invite.expires_at).getTime() < Date.now()) {
10148
+ throw new Error("Invitation expired");
10149
+ }
10150
+ if (invite.invitee_email && invite.invitee_email.toLowerCase() !== email.toLowerCase()) {
10151
+ throw new Error("Invitation is addressed to a different email");
10152
+ }
10153
+ const team = await db.get("__lattice_team", invite.team_id);
10154
+ if (!team || team.deleted_at) {
10155
+ throw new Error("Team no longer exists");
10156
+ }
10157
+ const now = (/* @__PURE__ */ new Date()).toISOString();
10158
+ const userId = await db.insert("__lattice_users", {
10159
+ email,
10160
+ name,
10161
+ created_at: now,
10162
+ updated_at: now
10163
+ });
10164
+ await db.insert("__lattice_team_members", {
10165
+ team_id: invite.team_id,
10166
+ user_id: userId,
10167
+ role: "member",
10168
+ joined_at: now
10169
+ });
10170
+ const { raw, hash } = generateToken();
10171
+ await db.insert("__lattice_api_tokens", {
10172
+ user_id: userId,
10173
+ token_hash: hash,
10174
+ name: `invited:${team.name}`,
10175
+ created_at: now
10176
+ });
10177
+ await db.update("__lattice_invitations", invite.id, {
10178
+ redeemed_at: now,
10179
+ redeemed_by_user_id: userId
10180
+ });
10181
+ return {
10182
+ user: { id: userId, email, name },
10183
+ raw_token: raw,
10184
+ team: { id: team.id, name: team.name }
10185
+ };
10186
+ } finally {
10187
+ try {
10188
+ db.close();
10189
+ } catch {
10190
+ }
10191
+ }
10192
+ }
10120
10193
 
10121
10194
  // src/teams/client.ts
10122
10195
  var TeamsClient = class {
@@ -10160,6 +10233,9 @@ var TeamsClient = class {
10160
10233
  });
10161
10234
  }
10162
10235
  async redeemInvite(cloudUrl, inviteToken, email, name) {
10236
+ if (isPostgresUrl(cloudUrl)) {
10237
+ return redeemInviteDirect(cloudUrl, inviteToken, email, name);
10238
+ }
10163
10239
  return this.fetchUnauthed(cloudUrl, "POST", "/api/auth/redeem-invite", {
10164
10240
  invite_token: inviteToken,
10165
10241
  email,
package/dist/index.cjs CHANGED
@@ -6053,6 +6053,75 @@ async function destroyTeamDirect(db) {
6053
6053
  }
6054
6054
  await db.delete("__lattice_team_identity", "singleton");
6055
6055
  }
6056
+ async function redeemInviteDirect(cloudUrl, inviteToken, email, name) {
6057
+ if (!isPostgresUrl(cloudUrl)) {
6058
+ throw new Error(
6059
+ `redeemInviteDirect: cloudUrl must be a postgres:// URL (got ${cloudUrl.slice(0, 12)}\u2026)`
6060
+ );
6061
+ }
6062
+ const db = new Lattice(cloudUrl);
6063
+ try {
6064
+ await db.init();
6065
+ for (const [table, def] of Object.entries(CLOUD_INTERNAL_TABLE_DEFS)) {
6066
+ await db.defineLate(table, def);
6067
+ }
6068
+ const invites = await db.query("__lattice_invitations", {
6069
+ filters: [
6070
+ { col: "token_hash", op: "eq", val: hashToken(inviteToken) },
6071
+ { col: "redeemed_at", op: "isNull" }
6072
+ ],
6073
+ limit: 1
6074
+ });
6075
+ const invite = invites[0];
6076
+ if (!invite) {
6077
+ throw new Error("Invitation invalid or already used");
6078
+ }
6079
+ if (invite.expires_at && new Date(invite.expires_at).getTime() < Date.now()) {
6080
+ throw new Error("Invitation expired");
6081
+ }
6082
+ if (invite.invitee_email && invite.invitee_email.toLowerCase() !== email.toLowerCase()) {
6083
+ throw new Error("Invitation is addressed to a different email");
6084
+ }
6085
+ const team = await db.get("__lattice_team", invite.team_id);
6086
+ if (!team || team.deleted_at) {
6087
+ throw new Error("Team no longer exists");
6088
+ }
6089
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6090
+ const userId = await db.insert("__lattice_users", {
6091
+ email,
6092
+ name,
6093
+ created_at: now,
6094
+ updated_at: now
6095
+ });
6096
+ await db.insert("__lattice_team_members", {
6097
+ team_id: invite.team_id,
6098
+ user_id: userId,
6099
+ role: "member",
6100
+ joined_at: now
6101
+ });
6102
+ const { raw, hash } = generateToken();
6103
+ await db.insert("__lattice_api_tokens", {
6104
+ user_id: userId,
6105
+ token_hash: hash,
6106
+ name: `invited:${team.name}`,
6107
+ created_at: now
6108
+ });
6109
+ await db.update("__lattice_invitations", invite.id, {
6110
+ redeemed_at: now,
6111
+ redeemed_by_user_id: userId
6112
+ });
6113
+ return {
6114
+ user: { id: userId, email, name },
6115
+ raw_token: raw,
6116
+ team: { id: team.id, name: team.name }
6117
+ };
6118
+ } finally {
6119
+ try {
6120
+ db.close();
6121
+ } catch {
6122
+ }
6123
+ }
6124
+ }
6056
6125
 
6057
6126
  // src/teams/client.ts
6058
6127
  var TeamsClient = class {
@@ -6096,6 +6165,9 @@ var TeamsClient = class {
6096
6165
  });
6097
6166
  }
6098
6167
  async redeemInvite(cloudUrl, inviteToken, email, name) {
6168
+ if (isPostgresUrl(cloudUrl)) {
6169
+ return redeemInviteDirect(cloudUrl, inviteToken, email, name);
6170
+ }
6099
6171
  return this.fetchUnauthed(cloudUrl, "POST", "/api/auth/redeem-invite", {
6100
6172
  invite_token: inviteToken,
6101
6173
  email,
package/dist/index.js CHANGED
@@ -5979,6 +5979,75 @@ async function destroyTeamDirect(db) {
5979
5979
  }
5980
5980
  await db.delete("__lattice_team_identity", "singleton");
5981
5981
  }
5982
+ async function redeemInviteDirect(cloudUrl, inviteToken, email, name) {
5983
+ if (!isPostgresUrl(cloudUrl)) {
5984
+ throw new Error(
5985
+ `redeemInviteDirect: cloudUrl must be a postgres:// URL (got ${cloudUrl.slice(0, 12)}\u2026)`
5986
+ );
5987
+ }
5988
+ const db = new Lattice(cloudUrl);
5989
+ try {
5990
+ await db.init();
5991
+ for (const [table, def] of Object.entries(CLOUD_INTERNAL_TABLE_DEFS)) {
5992
+ await db.defineLate(table, def);
5993
+ }
5994
+ const invites = await db.query("__lattice_invitations", {
5995
+ filters: [
5996
+ { col: "token_hash", op: "eq", val: hashToken(inviteToken) },
5997
+ { col: "redeemed_at", op: "isNull" }
5998
+ ],
5999
+ limit: 1
6000
+ });
6001
+ const invite = invites[0];
6002
+ if (!invite) {
6003
+ throw new Error("Invitation invalid or already used");
6004
+ }
6005
+ if (invite.expires_at && new Date(invite.expires_at).getTime() < Date.now()) {
6006
+ throw new Error("Invitation expired");
6007
+ }
6008
+ if (invite.invitee_email && invite.invitee_email.toLowerCase() !== email.toLowerCase()) {
6009
+ throw new Error("Invitation is addressed to a different email");
6010
+ }
6011
+ const team = await db.get("__lattice_team", invite.team_id);
6012
+ if (!team || team.deleted_at) {
6013
+ throw new Error("Team no longer exists");
6014
+ }
6015
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6016
+ const userId = await db.insert("__lattice_users", {
6017
+ email,
6018
+ name,
6019
+ created_at: now,
6020
+ updated_at: now
6021
+ });
6022
+ await db.insert("__lattice_team_members", {
6023
+ team_id: invite.team_id,
6024
+ user_id: userId,
6025
+ role: "member",
6026
+ joined_at: now
6027
+ });
6028
+ const { raw, hash } = generateToken();
6029
+ await db.insert("__lattice_api_tokens", {
6030
+ user_id: userId,
6031
+ token_hash: hash,
6032
+ name: `invited:${team.name}`,
6033
+ created_at: now
6034
+ });
6035
+ await db.update("__lattice_invitations", invite.id, {
6036
+ redeemed_at: now,
6037
+ redeemed_by_user_id: userId
6038
+ });
6039
+ return {
6040
+ user: { id: userId, email, name },
6041
+ raw_token: raw,
6042
+ team: { id: team.id, name: team.name }
6043
+ };
6044
+ } finally {
6045
+ try {
6046
+ db.close();
6047
+ } catch {
6048
+ }
6049
+ }
6050
+ }
5982
6051
 
5983
6052
  // src/teams/client.ts
5984
6053
  var TeamsClient = class {
@@ -6022,6 +6091,9 @@ var TeamsClient = class {
6022
6091
  });
6023
6092
  }
6024
6093
  async redeemInvite(cloudUrl, inviteToken, email, name) {
6094
+ if (isPostgresUrl(cloudUrl)) {
6095
+ return redeemInviteDirect(cloudUrl, inviteToken, email, name);
6096
+ }
6025
6097
  return this.fetchUnauthed(cloudUrl, "POST", "/api/auth/redeem-invite", {
6026
6098
  invite_token: inviteToken,
6027
6099
  email,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "latticesql",
3
- "version": "1.13.4",
3
+ "version": "1.13.5",
4
4
  "description": "Persistent structured memory for AI agent systems — pluggable SQLite or Postgres backend, LLM context bridge",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",