# Semantic · 访问与账号（原权限 SDK）

> 1.20.0 起权限 SDK 并进 [Semantic](semantic.md)：资源的访问是 `semantic.filesystem`（Private / Group / Public / Open to Internet，`aidc semantic filesystem …`），账号与成员是 `semantic.admin`（`getCurrentUser`、`signIn`、`members`，`aidc semantic admin …`），应用分享是 `semantic.filesystem.shareApplication`。本页的 `auth.*`、`aidc share / members / resources` 照样能用。


用 AIDC 自己的账号体系做应用的**登录、身份、角色与分享**——不必自己写登录页、不必自己存用户。

```js
import { auth } from "/developer/sdk/v1/aidc.js";
```

## 登录与身份

Nexus 客户应用（`/nexus/<cellId>/apps/<应用>`）打开前，平台已经按会话把关：没登录跳 AIDC 登录页、登录后回到应用；不是这家公司的人（也没有被分享）看到 403。访客身份随**应用票据**下发，服务端按它裁决每一次读写——应用自己不用、也不能伪造身份。

```js
const v = auth.viewer();        // 同步读：{ name: "张三", role: "member", shared: false }（页面注入，用来显示）
const me = await auth.me();     // 服务端确认：账号、公司、角色、应用 / 通道 / 版本，以及语义层权限
// me.permissions = { read: [可读类型], actions: [能执行的 Action], write: [可直接写的类型], share: 能否分享 }
if (auth.can(me, { action: "production.flag_issue" })) showFlagButton();
```

## 角色

| 角色 | 谁 | 能做什么 |
| --- | --- | --- |
| `developer` | 本公司开发者（持 developer License 的账号、公司管理员、AIDC 平台员工） | 看全部；定义语义；直接改数据；分享；test 通道 |
| `member` | 本公司成员（持 member License 或公司账号） | 看清单登记的类型；执行 Action |
| `editor` | 经「指定账号」分享、角色为可改的人（可以是别家公司的人） | 同 member |
| `viewer` | 只读分享、公开链接的访客 | 只看 |
| `anonymous` | AIDC 官方公开应用的访客 | 只看公开数据 |

应用清单可以把应用收紧到只有开发者和被分享的人：

```json
{ "auth": { "access": "restricted" } }
```

缺省 `company`：本公司成员都能打开。Action 自己也可以限定角色（`roles`，见语义 SDK）。

## 登录绑定：公开的应用，也知道「你是不是本公司的人」

应用通过**公开链接**（`aidc share <应用> --public`）对外开放时，访客也要先用 AIDC 账号登录（一律登录，2026-09-29 起没有匿名访客；邮箱验证码，公司邮箱或任何邮箱都行）。登录后票据带上他的账号与公司身份：本公司的人按 `member` / `developer` 权限，其他人只读，但应用知道他是谁。

```js
const me = await auth.me();
// me.signedIn：登录了没有；me.member：是不是这个应用所在公司的人（developer / member）
if (!me.signedIn) auth.signIn();                       // 整页去 AIDC 登录，登录后回到当前链接
else if (!me.member) ui.toast("仅限本公司成员使用");     // 登录了但不是这家公司的人
```

清单 `auth.publicLink` 让平台在打开之前就把关（不用应用自己判断）：

| 值 | 持公开链接的访客 |
| --- | --- |
| `signin`（缺省） | 先用 AIDC 账号登录再打开；本公司的人按公司角色，不是本公司的人只读 |
| `members` | 登录后只有本公司的人（developer / member，或被指定账号分享的人）能打开，其他人看到 403 |
| `open`（旧值） | 一律登录起按 `signin` 执行，只为旧清单保留 |

```json
{ "auth": { "access": "company", "publicLink": "members" } }
```

`auth.signIn(returnTo?)` 是整页跳转（缺省回到当前地址）；旧的 `auth.login()` 在新窗口里登录、回到应用，需要留在当前页时用它。

## 成员：把已有的用户名单导进来

公司在别的系统里已经有一批注册用户（例如旧门户 / 老系统的员工账号）时，开发者可以把名单导入 AIDC——导入的人都成为本公司的 **member**：

```bash
aidc members import users.json --source old-portal --dry-run   # 先看每个人会怎么处理
aidc members import users.json --source old-portal
aidc members list                                                  # 本公司有哪些人、谁还没激活
```

```js
const plan = await auth.importMembers({ source: "old-portal", members: [{ email: "zhang.san@example.com", name: "张三", externalId: "user_2x…" }], dryRun: true });
// plan.summary = { create, grant, keep, keep_developer, skip_disabled, conflict }；plan.seats = { limit, used, after }
```

| 情况 | 处理 |
| --- | --- |
| AIDC 里没有这个邮箱 | 建一个**待激活**账号（没有口令、邮箱未验证、登不进来）+ 本公司 member License |
| 有账号但还不是本公司的人 | 补一张本公司 member License |
| 已经是本公司 member | 不动 |
| 已经是本公司 **developer**（或 AIDC 平台员工） | 不动，绝不降级 |
| 账号已停用 | 不动 |

- 每人占一个席位（公司 License 额度的 seats），席位不够整批拒绝；`--dry-run` 会显示席位用量。
- 名单：JSON（`[{ email, name?, externalId? }]` 或 `{ "members": [...] }`）或 CSV（表头含 `email`，可有 `name`、`externalId`），一次 ≤ 1000 人。`externalId` 记下来源系统里的 id，同一个人重复导入不会重复建号；重复导入整体幂等。
- **本人第一次用这个邮箱在 AIDC 登录（收验证码）即认领账号**：账号激活、邮箱算验证过，公司身份跟着 License 走。导入不会替本人验证邮箱，也不会给别家公司的权限。原系统的口令不迁移——AIDC 用邮箱验证码登录，登录后可以自己设口令。

## 分享

```js
// 开发者：分享当前应用
await auth.share({ audience: "company", role: "editor" });                       // 本公司全员（restricted 应用靠它对全员开放）
await auth.share({ audience: "users", account: "li.si@example.com", role: "editor", expiresInDays: 30 }); // 指定账号，可跨公司
const { url } = await auth.share({ audience: "public" });                         // 公开链接：持链接、登录了的人，只读
const list = await auth.shares();                                                 // 当前应用的分享（公开链接只显示前缀）
await auth.revoke(list[0].id);                                                    // 撤销：链接 / 授权立即失效
```

- **公开链接**只读，完整地址只在创建时返回一次（库里只存哈希）。链接打开的是应用 production 通道的当前版本，要先登录 AIDC 账号（一律登录）；想修改还要被分享为 editor。**分享的是真实数据**，发之前确认它可以对外。
- company / users 对同一对象再分享 = 改角色与有效期（幂等）；public 每次一条新链接。
- 每次分享、撤销都进审计（ActionLog）与应用日志（`kind: share`）。

```bash
aidc share production-live --user li.si@example.com --role editor --days 30
aidc share production-live --public
aidc share list production-live
aidc share revoke <分享 id>
```

## 访问：Private / Group / Public / Open to Internet（所有资源）

AIDC 的资源——Ontology、Object Type、数字员工、文件——用同一套访问设置（照 Palantir：Organization、Space、角色、Share 面板，`docs/access-model.md`）。
**开放程度四档，每一档比上一档更开：**

| 档 | 谁能看 |
| --- | --- |
| **Private** | 只有 Owner（可以有多个） |
| **Group** | 指定的人、部门、整个本组织（邀请链接也属于 Group） |
| **Public** | 所有登录的 AIDC 账号（跨组织），只读；不含匿名 |
| **Open to Internet** | 任何人，不用登录，只读；默认关闭，只有 Owner 能开；目前只有文件 |

- **组织 = 公司**，是默认的边界，数据按组织隔离。「整个本组织」是 Group 里最常用的分享对象。
- **角色**：**Owner**（改分享与访问设置、添加其他 Owner）· **Editor**（改内容：Ontology 里执行 Action 改数据、文件里编辑；不能改分享）· **Viewer**（查看、使用）。本组织开发者永远是 Owner；创建人是 Owner。
- **规则**：**只有 Owner 能改分享**；Public 与 Open to Internet 只能给 Viewer；Open to Internet 只给有匿名只读入口的资源类型（目前是文件）；Ontology 上的角色继承到它的 Object Type，Object Type 自己的授予只加不减。
- **Open to Internet 只管读**：不登录不能调用 SDK、不能执行 Action、不能查 SQL。
- **邀请链接**：持链接的人要先登录 AIDC 账号，打开 `/share/<令牌>` 后得到一条 Viewer 授予；链接可撤销，缺省 30 天。
- **分享给谁**：具体的人（账号 id）、一个部门（`cell-<公司>:dept:<部门 ID>`）、整个本组织（`cell-<公司>`）、本组织开发者（`cell-<公司>:developers`）；别的组织只按 cellId 精确找。

```js
const pub = await auth.resources("public");                  // 所有 AIDC 账号都看得见的
const mine = await auth.resources("shared");                 // 别的组织分享给我的
await auth.setResourceAccess(rid, "group");                  // 一步设档位（Owner）：private | group | public | internet
await auth.shareResource(rid, { user: "<账号 id>", role: "editor" });
await auth.shareResource(rid, { group: "cell-acme:dept:ops", role: "viewer" });   // 分享给一个部门
await auth.shareResource(rid, { everyone: true });           // 加上 Public 的授予
await auth.removeResourceRoles(rid, [{ resourceRolePrincipal: { type: "everyone" }, roleId: "viewer" }]);
const { url } = await auth.resourceLink(rid, { expiresInDays: 7 });   // 邀请链接
```

`setResourceAccess` 是一步到位的写法：`private` 撤掉所有分享，只剩 Owner；`group` 撤掉 Public 与 Internet，还没分享给任何人时加整个本组织 Viewer；
`public` 加 Everyone Viewer；`internet` 加 Internet Viewer（只有文件）。资源自带的设置是下限（智能体的可见性、文件的受众）：调得比它更窄会返回 409，先去它自己的设置里改。

```bash
aidc resources list --scope public
aidc resources roles <rid>
aidc resources access <rid> --tier group
aidc resources share <rid> --user <账号 id> --role editor
aidc resources share <rid> --group cell-acme:dept:ops --role viewer
aidc resources share <rid> --everyone
aidc resources unshare <rid> --everyone --role viewer
aidc resources link <rid> --days 7
```

RID 的形状：Ontology `ri.ontology.aidc.ontology.<cell>`、Object Type `ri.ontology.aidc.object-type.<entityId>`、数字员工 `ri.agents.aidc.agent.<id>`、文件 `ri.compass.aidc.file.<id>`。网页上每个资源都有自己的访问面板：`/semantic/<cell>/access`、Object Type 页、`/semantic/resources/<rid>`。

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/api/v1/filesystem/resources?scope=public\|organization\|shared\|mine&type=…` | 看得见的资源 |
| GET | `/api/v1/filesystem/resources/{rid}` | 资源、我的角色、开放程度 `generalAccess`（`private` / `group` / `public` / `internet`；Palantir getResource） |
| GET | `/api/v1/filesystem/resources/{rid}/roles` | 授予与隐式规则（listResourceRoles） |
| POST | `/api/v1/filesystem/resources/{rid}/roles/add`、`/roles/remove` | 授予 / 撤销（addResourceRoles / removeResourceRoles；主体 `principalWithId` · `everyone` · `internet`） |
| POST | `/api/v1/filesystem/resources/{rid}/access` | 一步设档位 `{"tier":"private\|group\|public\|internet"}`（Owner） |
| POST / DELETE | `/api/v1/filesystem/resources/{rid}/links[/{linkId}]` | 建 / 撤销邀请链接 |
| POST | `/api/v1/filesystem/links/redeem` | 兑换邀请链接 |
| GET | `/api/v1/filesystem/principals?q=` | 分享给谁：本组织的全员组、开发者组、各部门；账号只按用户名 / 邮箱精确匹配；别的组织只按 cellId |

这一套管的是资源本身；Nexus 应用的分享仍用上面「分享」一节（`auth.share`）。

## 票据

浏览器 SDK 自动带票据、自动续期：客户应用的票据 8 小时有效，快到期时凭当前票据续一张（过期 24 小时内也能续，服务端会复核账号仍有权访问、分享仍然有效）。所以工厂大屏这种一直开着的看板不会过夜就断。

## API

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/api/v1/developer/auth/me` | 我是谁、什么角色、语义层权限 |
| GET / POST | `/api/v1/developer/shares` | 分享列表 / 分享（开发者） |
| DELETE | `/api/v1/developer/shares/{id}` | 撤销 |
| GET | `/nexus/s/{令牌}` | 公开链接落地页（先登录；登录后的访客带上身份，外人只读，清单 `auth.publicLink: members` 只给本公司成员） |
| GET | `/api/v1/developer/members` | 本公司成员（开发者） |
| POST | `/api/v1/developer/members/import` | 导入已有名单（开发者；支持 dry-run、幂等） |
