feat(identity): lock NoteLett product identity across all surfaces
- productId: notelett - displayName: NoteLett - domain: notelett.app - iOS bundle: com.bytelyst.notelett - Android bundle: com.notelett.app - backend port: 4016 - token namespace: --nl-* (CSS), NoteLettTheme (native) Rippled through: - shared/product.json (canonical source) - backend package.json, config, cosmos-init, all 10 test files - web package.json, landing page, notes-client test - mobile app.json, package.json, auth screen - docs: PRD, ROADMAP, architecture review, foundations, web/mobile roadmaps - registered in learning_ai_common_plat/products/notelett/ Verification: backend typecheck + 18 tests, web typecheck + 6 tests, mobile typecheck — all pass.
This commit is contained in:
parent
e6beef83eb
commit
e1fde25afd
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "@bytelyst-notes/backend",
|
"name": "@notelett/backend",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "ByteLyst Agentic Notes product-specific backend — notes, workspaces, relationships, tasks, artifacts, agent actions",
|
"description": "NoteLett product backend — notes, workspaces, relationships, tasks, artifacts, agent actions",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsx watch src/server.ts",
|
"dev": "tsx watch src/server.ts",
|
||||||
|
|||||||
@ -6,7 +6,7 @@ const envSchema = z.object({
|
|||||||
HOST: z.string().default('0.0.0.0'),
|
HOST: z.string().default('0.0.0.0'),
|
||||||
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
||||||
CORS_ORIGIN: z.string().optional(),
|
CORS_ORIGIN: z.string().optional(),
|
||||||
SERVICE_NAME: z.string().default('bytelyst-notes-backend'),
|
SERVICE_NAME: z.string().default('notelett-backend'),
|
||||||
COSMOS_ENDPOINT: z.string().min(1, 'COSMOS_ENDPOINT is required').optional(),
|
COSMOS_ENDPOINT: z.string().min(1, 'COSMOS_ENDPOINT is required').optional(),
|
||||||
COSMOS_KEY: z.string().min(1, 'COSMOS_KEY is required').optional(),
|
COSMOS_KEY: z.string().min(1, 'COSMOS_KEY is required').optional(),
|
||||||
COSMOS_DATABASE: z.string().default('bytelyst'),
|
COSMOS_DATABASE: z.string().default('bytelyst'),
|
||||||
|
|||||||
@ -21,9 +21,9 @@ export async function initCosmosIfNeeded(): Promise<void> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await initializeAllContainers();
|
await initializeAllContainers();
|
||||||
process.stdout.write('[bytelyst-notes-backend] Cosmos containers ensured\n');
|
process.stdout.write('[notelett-backend] Cosmos containers ensured\n');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err instanceof Error ? err.message : String(err);
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
process.stderr.write(`[bytelyst-notes-backend] Cosmos init failed: ${msg}\n`);
|
process.stderr.write(`[notelett-backend] Cosmos init failed: ${msg}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ const { listNotesMock, getNoteMock, createNoteMock, createNoteAgentActionMock }
|
|||||||
createNoteAgentActionMock: vi.fn(),
|
createNoteAgentActionMock: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' }));
|
vi.mock('../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' }));
|
||||||
vi.mock('../modules/notes/repository.js', () => ({
|
vi.mock('../modules/notes/repository.js', () => ({
|
||||||
listNotes: listNotesMock,
|
listNotes: listNotesMock,
|
||||||
getNote: getNoteMock,
|
getNote: getNoteMock,
|
||||||
@ -23,7 +23,7 @@ import { NotesExecutableMcpTools, getNotesExecutableMcpTool } from './note-tools
|
|||||||
const req = {
|
const req = {
|
||||||
id: 'req_1',
|
id: 'req_1',
|
||||||
headers: {},
|
headers: {},
|
||||||
jwtPayload: { sub: 'user_1', role: 'admin', productId: 'bytelyst-notes' },
|
jwtPayload: { sub: 'user_1', role: 'admin', productId: 'notelett' },
|
||||||
log: {
|
log: {
|
||||||
info: vi.fn(),
|
info: vi.fn(),
|
||||||
warn: vi.fn(),
|
warn: vi.fn(),
|
||||||
@ -51,7 +51,7 @@ describe('note executable MCP tools', () => {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 'note_1',
|
id: 'note_1',
|
||||||
productId: 'bytelyst-notes',
|
productId: 'notelett',
|
||||||
workspaceId: 'ws_1',
|
workspaceId: 'ws_1',
|
||||||
userId: 'user_1',
|
userId: 'user_1',
|
||||||
title: 'Draft',
|
title: 'Draft',
|
||||||
@ -74,7 +74,7 @@ describe('note executable MCP tools', () => {
|
|||||||
req
|
req
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(listNotesMock).toHaveBeenCalledWith('user_1', 'bytelyst-notes', {
|
expect(listNotesMock).toHaveBeenCalledWith('user_1', 'notelett', {
|
||||||
workspaceId: 'ws_1',
|
workspaceId: 'ws_1',
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
@ -87,7 +87,7 @@ describe('note executable MCP tools', () => {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 'note_1',
|
id: 'note_1',
|
||||||
productId: 'bytelyst-notes',
|
productId: 'notelett',
|
||||||
workspaceId: 'ws_1',
|
workspaceId: 'ws_1',
|
||||||
userId: 'user_1',
|
userId: 'user_1',
|
||||||
title: 'Retention policy',
|
title: 'Retention policy',
|
||||||
@ -119,7 +119,7 @@ describe('note executable MCP tools', () => {
|
|||||||
it('gets a scoped note', async () => {
|
it('gets a scoped note', async () => {
|
||||||
getNoteMock.mockResolvedValue({
|
getNoteMock.mockResolvedValue({
|
||||||
id: 'note_1',
|
id: 'note_1',
|
||||||
productId: 'bytelyst-notes',
|
productId: 'notelett',
|
||||||
workspaceId: 'ws_1',
|
workspaceId: 'ws_1',
|
||||||
userId: 'user_1',
|
userId: 'user_1',
|
||||||
title: 'Note',
|
title: 'Note',
|
||||||
@ -180,7 +180,7 @@ describe('note executable MCP tools', () => {
|
|||||||
expect(createNoteAgentActionMock).toHaveBeenCalledTimes(1);
|
expect(createNoteAgentActionMock).toHaveBeenCalledTimes(1);
|
||||||
expect(createNoteAgentActionMock).toHaveBeenCalledWith(
|
expect(createNoteAgentActionMock).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
productId: 'bytelyst-notes',
|
productId: 'notelett',
|
||||||
workspaceId: 'ws_1',
|
workspaceId: 'ws_1',
|
||||||
userId: 'user_1',
|
userId: 'user_1',
|
||||||
actorId: 'agent_1',
|
actorId: 'agent_1',
|
||||||
|
|||||||
@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
||||||
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' }));
|
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' }));
|
||||||
vi.mock('./repository.js', () => ({
|
vi.mock('./repository.js', () => ({
|
||||||
listNoteAgentActions: vi.fn(async () => ({ items: [], total: 0 })),
|
listNoteAgentActions: vi.fn(async () => ({ items: [], total: 0 })),
|
||||||
getNoteAgentAction: vi.fn(async () => null),
|
getNoteAgentAction: vi.fn(async () => null),
|
||||||
|
|||||||
@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
||||||
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' }));
|
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' }));
|
||||||
vi.mock('./repository.js', () => ({
|
vi.mock('./repository.js', () => ({
|
||||||
listNoteArtifacts: vi.fn(async () => ({ items: [], total: 0 })),
|
listNoteArtifacts: vi.fn(async () => ({ items: [], total: 0 })),
|
||||||
getNoteArtifact: vi.fn(async () => null),
|
getNoteArtifact: vi.fn(async () => null),
|
||||||
|
|||||||
@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
||||||
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' }));
|
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' }));
|
||||||
vi.mock('./repository.js', () => ({
|
vi.mock('./repository.js', () => ({
|
||||||
listRelationships: vi.fn(async () => ({ items: [], total: 0 })),
|
listRelationships: vi.fn(async () => ({ items: [], total: 0 })),
|
||||||
createRelationship: vi.fn(async (doc: unknown) => doc),
|
createRelationship: vi.fn(async (doc: unknown) => doc),
|
||||||
|
|||||||
@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
||||||
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' }));
|
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' }));
|
||||||
vi.mock('./repository.js', () => ({
|
vi.mock('./repository.js', () => ({
|
||||||
listNoteTasks: vi.fn(async () => ({ items: [], total: 0 })),
|
listNoteTasks: vi.fn(async () => ({ items: [], total: 0 })),
|
||||||
getNoteTask: vi.fn(async () => null),
|
getNoteTask: vi.fn(async () => null),
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
||||||
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' }));
|
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' }));
|
||||||
vi.mock('./repository.js', () => ({
|
vi.mock('./repository.js', () => ({
|
||||||
listNotes: listNotesMock,
|
listNotes: listNotesMock,
|
||||||
getNote: getNoteMock,
|
getNote: getNoteMock,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ const { extractAuthMock } = vi.hoisted(() => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
vi.mock('../../lib/auth.js', () => ({ extractAuth: extractAuthMock }));
|
||||||
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'bytelyst-notes' }));
|
vi.mock('../../lib/product-config.js', () => ({ PRODUCT_ID: 'notelett' }));
|
||||||
vi.mock('./repository.js', () => ({
|
vi.mock('./repository.js', () => ({
|
||||||
listWorkspaces: vi.fn(async () => ({ items: [], total: 0 })),
|
listWorkspaces: vi.fn(async () => ({ items: [], total: 0 })),
|
||||||
getWorkspace: vi.fn(async () => null),
|
getWorkspace: vi.fn(async () => null),
|
||||||
|
|||||||
@ -17,7 +17,7 @@ vi.mock('@bytelyst/fastify-core', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('jose', () => ({
|
vi.mock('jose', () => ({
|
||||||
jwtVerify: vi.fn(async () => ({ payload: { sub: 'user_1', productId: 'bytelyst-notes' } })),
|
jwtVerify: vi.fn(async () => ({ payload: { sub: 'user_1', productId: 'notelett' } })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('./modules/note-agent-actions/routes.js', () => ({ noteAgentActionRoutes: vi.fn() }));
|
vi.mock('./modules/note-agent-actions/routes.js', () => ({ noteAgentActionRoutes: vi.fn() }));
|
||||||
@ -30,14 +30,14 @@ vi.mock('./lib/cosmos-init.js', () => ({ initCosmosIfNeeded: initCosmosIfNeededM
|
|||||||
vi.mock('./lib/datastore.js', () => ({ initDatastore: initDatastoreMock }));
|
vi.mock('./lib/datastore.js', () => ({ initDatastore: initDatastoreMock }));
|
||||||
vi.mock('./lib/config.js', () => ({
|
vi.mock('./lib/config.js', () => ({
|
||||||
config: {
|
config: {
|
||||||
SERVICE_NAME: 'bytelyst-notes-backend',
|
SERVICE_NAME: 'notelett-backend',
|
||||||
CORS_ORIGIN: '*',
|
CORS_ORIGIN: '*',
|
||||||
PORT: 4016,
|
PORT: 4016,
|
||||||
HOST: '0.0.0.0',
|
HOST: '0.0.0.0',
|
||||||
JWT_SECRET: 'test-secret',
|
JWT_SECRET: 'test-secret',
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
vi.mock('./lib/product-config.js', () => ({ DISPLAY_NAME: 'ByteLyst Agentic Notes' }));
|
vi.mock('./lib/product-config.js', () => ({ DISPLAY_NAME: 'NoteLett' }));
|
||||||
|
|
||||||
describe('server bootstrap', () => {
|
describe('server bootstrap', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# ByteLyst Agentic Notes — Architecture Review, Gap Analysis, and Reuse-First Action Plan
|
# NoteLett — Architecture Review, Gap Analysis, and Reuse-First Action Plan
|
||||||
|
|
||||||
Version: 1.0
|
Version: 1.0
|
||||||
Date: March 10, 2026
|
Date: March 10, 2026
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
Product Requirements Document: ByteLyst Agentic Notes
|
Product Requirements Document: NoteLett
|
||||||
|
|
||||||
Version: 2.0
|
Version: 2.0
|
||||||
Date: March 10, 2026
|
Date: March 10, 2026
|
||||||
@ -7,7 +7,7 @@ Status: Draft
|
|||||||
|
|
||||||
# 1. Product Vision
|
# 1. Product Vision
|
||||||
|
|
||||||
ByteLyst Agentic Notes is a knowledge product for humans and AI agents to capture, structure, retrieve, and operationalize notes in a shared system of record.
|
NoteLett is a knowledge product for humans and AI agents to capture, structure, retrieve, and operationalize notes in a shared system of record.
|
||||||
|
|
||||||
This is not just a notes UI with an AI chat panel bolted on. It is a product-specific knowledge application that fits the existing ByteLyst ecosystem:
|
This is not just a notes UI with an AI chat panel bolted on. It is a product-specific knowledge application that fits the existing ByteLyst ecosystem:
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# ByteLyst Agentic Notes — Master Roadmap
|
# NoteLett — Master Roadmap
|
||||||
|
|
||||||
Version: 2.0
|
Version: 2.0
|
||||||
Date: March 10, 2026
|
Date: March 10, 2026
|
||||||
|
|||||||
@ -60,9 +60,9 @@ Make the product implementation-ready and eliminate ambiguity before parallel co
|
|||||||
- 2026-03-10 — Phase 0 decisions were partially locked to unblock implementation.
|
- 2026-03-10 — Phase 0 decisions were partially locked to unblock implementation.
|
||||||
- The repo now has active `web/`, `backend/`, and `shared/` surfaces instead of a docs-only structure.
|
- The repo now has active `web/`, `backend/`, and `shared/` surfaces instead of a docs-only structure.
|
||||||
- Provisional bootstrap values are in use for implementation:
|
- Provisional bootstrap values are in use for implementation:
|
||||||
- product display name: `ByteLyst Agentic Notes`
|
- product display name: `NoteLett`
|
||||||
- provisional `productId`: `bytelyst-notes`
|
- `productId`: `notelett`
|
||||||
- provisional domain: `notes.bytelyst.app`
|
- domain: `notelett.app`
|
||||||
- backend port: `4016`
|
- backend port: `4016`
|
||||||
- Backend scaffolding now uses the established ByteLyst product-backend pattern:
|
- Backend scaffolding now uses the established ByteLyst product-backend pattern:
|
||||||
- Fastify 5
|
- Fastify 5
|
||||||
@ -72,8 +72,8 @@ Make the product implementation-ready and eliminate ambiguity before parallel co
|
|||||||
|
|
||||||
# Open Questions
|
# Open Questions
|
||||||
|
|
||||||
- Should the final canonical `productId` remain `bytelyst-notes` or use a shorter ecosystem-standard form?
|
- ~~Should the final canonical `productId` remain `bytelyst-notes` or use a shorter ecosystem-standard form?~~ **Resolved:** `notelett`
|
||||||
- Should the final public domain be `notes.bytelyst.app` or another product-specific domain?
|
- ~~Should the final public domain be `notes.bytelyst.app` or another product-specific domain?~~ **Resolved:** `notelett.app`
|
||||||
- What is the final operator vs shared-admin boundary for approval and audit review flows?
|
- What is the final operator vs shared-admin boundary for approval and audit review flows?
|
||||||
- Does v1 mobile parity include approvals and lightweight editing, or capture/retrieval only?
|
- Does v1 mobile parity include approvals and lightweight editing, or capture/retrieval only?
|
||||||
- What token namespace should be requested for shared design-system generation?
|
- What token namespace should be requested for shared design-system generation?
|
||||||
|
|||||||
@ -189,10 +189,7 @@ Stack: Next.js 16 + React 19 + TypeScript
|
|||||||
|
|
||||||
# Blockers
|
# Blockers
|
||||||
|
|
||||||
- Product identity is still draft-level in the planning docs, so the scaffold currently uses provisional values:
|
- ~~Product identity is still draft-level in the planning docs~~ **Resolved:** product identity locked as NoteLett (`notelett`, port 4016, `notelett.app`)
|
||||||
- `ByteLyst Agentic Notes`
|
|
||||||
- `agentic-notes`
|
|
||||||
- `4016` as a placeholder notes API port in `.env.example`
|
|
||||||
- Backend integration contracts are only partially aligned with the web shell, so some routes still rely on demo auth fallback and client-derived operator/saved-view summaries.
|
- Backend integration contracts are only partially aligned with the web shell, so some routes still rely on demo auth fallback and client-derived operator/saved-view summaries.
|
||||||
|
|
||||||
# Deferred
|
# Deferred
|
||||||
|
|||||||
@ -96,8 +96,8 @@ Stack: React Native + Expo + TypeScript
|
|||||||
|
|
||||||
# Open Questions
|
# Open Questions
|
||||||
|
|
||||||
- Should the current provisional bootstrap values (`productId: bytelyst-notes`, backend port `4016`) now be treated as final?
|
- ~~Should the current provisional bootstrap values be treated as final?~~ **Resolved:** `productId: notelett`, port 4016, domain `notelett.app`
|
||||||
- What are the final iOS bundle identifier, Android package name, and URL scheme/domain values?
|
- ~~What are the final iOS bundle identifier, Android package name, and URL scheme/domain values?~~ **Resolved:** iOS `com.bytelyst.notelett`, Android `com.notelett.app`, scheme `notelett`
|
||||||
- Should mobile notes access go directly to the product backend for note CRUD while auth remains on `platform-service`?
|
- Should mobile notes access go directly to the product backend for note CRUD while auth remains on `platform-service`?
|
||||||
- Should the current local approval/activity model map directly to backend `note-agent-actions`, or stay as a mobile-specific condensed surface?
|
- Should the current local approval/activity model map directly to backend `note-agent-actions`, or stay as a mobile-specific condensed surface?
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"expo": {
|
"expo": {
|
||||||
"name": "ByteLyst Agentic Notes",
|
"name": "NoteLett",
|
||||||
"slug": "bytelyst-agentic-notes",
|
"slug": "notelett",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"userInterfaceStyle": "dark",
|
"userInterfaceStyle": "dark",
|
||||||
"scheme": "bytelyst-notes",
|
"scheme": "notelett",
|
||||||
"plugins": ["expo-router"],
|
"plugins": ["expo-router"],
|
||||||
"ios": {
|
"ios": {
|
||||||
"supportsTablet": true,
|
"supportsTablet": true,
|
||||||
"bundleIdentifier": "com.bytelyst.agenticnotes"
|
"bundleIdentifier": "com.bytelyst.notelett"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"package": "com.bytelyst.agenticnotes"
|
"package": "com.notelett.app"
|
||||||
},
|
},
|
||||||
"web": {
|
"web": {
|
||||||
"bundler": "metro"
|
"bundler": "metro"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "bytelyst-agentic-notes-mobile",
|
"name": "@notelett/mobile",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
|
|||||||
@ -12,6 +12,8 @@ export type MobileApprovalItem = {
|
|||||||
|
|
||||||
export type MobileActivityItem = {
|
export type MobileActivityItem = {
|
||||||
id: string;
|
id: string;
|
||||||
|
workspaceId: string;
|
||||||
|
noteId: string;
|
||||||
title: string;
|
title: string;
|
||||||
summary: string;
|
summary: string;
|
||||||
kind: 'note' | 'task' | 'agent';
|
kind: 'note' | 'task' | 'agent';
|
||||||
@ -71,6 +73,8 @@ function toActivityKind(actionType: NoteAgentActionDoc['actionType']): MobileAct
|
|||||||
function toActivityItem(action: NoteAgentActionDoc): MobileActivityItem {
|
function toActivityItem(action: NoteAgentActionDoc): MobileActivityItem {
|
||||||
return {
|
return {
|
||||||
id: action.id,
|
id: action.id,
|
||||||
|
workspaceId: action.workspaceId,
|
||||||
|
noteId: action.noteId,
|
||||||
title: action.afterSummary ?? `${action.actionType.replaceAll('_', ' ')} update`,
|
title: action.afterSummary ?? `${action.actionType.replaceAll('_', ' ')} update`,
|
||||||
summary: action.reason ?? action.afterSummary ?? `State: ${action.state}`,
|
summary: action.reason ?? action.afterSummary ?? `State: ${action.state}`,
|
||||||
kind: toActivityKind(action.actionType),
|
kind: toActivityKind(action.actionType),
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export default function AuthScreen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>ByteLyst Agentic Notes</Text>
|
<Text style={styles.title}>NoteLett</Text>
|
||||||
<Text style={styles.subtitle}>Mobile MVP auth shell</Text>
|
<Text style={styles.subtitle}>Mobile MVP auth shell</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
|
|||||||
@ -1,9 +1,16 @@
|
|||||||
{
|
{
|
||||||
"productId": "bytelyst-notes",
|
"productId": "notelett",
|
||||||
"displayName": "ByteLyst Agentic Notes",
|
"displayName": "NoteLett",
|
||||||
"licensePrefix": "NOTES",
|
"licensePrefix": "NOTELETT",
|
||||||
"configDirName": ".ByteLystNotes",
|
"configDirName": ".NoteLett",
|
||||||
"envVarPrefix": "NOTES",
|
"envVarPrefix": "NOTELETT",
|
||||||
"bundleIdSuffix": "Notes",
|
"bundleIdSuffix": "notelett",
|
||||||
"packageName": "bytelyst-notes"
|
"packageName": "notelett",
|
||||||
|
"domain": "notelett.app",
|
||||||
|
"bundleId": {
|
||||||
|
"ios": "com.bytelyst.notelett",
|
||||||
|
"android": "com.notelett.app"
|
||||||
|
},
|
||||||
|
"appGroup": "group.com.bytelyst.notelett",
|
||||||
|
"backendPort": 4016
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "bytelyst-agentic-notes-web",
|
"name": "@notelett/web",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -7,10 +7,10 @@ export default function HomePage() {
|
|||||||
<div className="badge">Backend-backed web surface</div>
|
<div className="badge">Backend-backed web surface</div>
|
||||||
<div style={{ display: "grid", gap: "var(--ml-space-3)" }}>
|
<div style={{ display: "grid", gap: "var(--ml-space-3)" }}>
|
||||||
<h1 style={{ margin: 0, fontFamily: "var(--ml-font-display)", fontSize: "var(--ml-fs-3xl)" }}>
|
<h1 style={{ margin: 0, fontFamily: "var(--ml-font-display)", fontSize: "var(--ml-fs-3xl)" }}>
|
||||||
ByteLyst Agentic Notes web surface
|
NoteLett
|
||||||
</h1>
|
</h1>
|
||||||
<p style={{ margin: 0, color: "var(--ml-text-secondary)", lineHeight: 1.6 }}>
|
<p style={{ margin: 0, color: "var(--ml-text-secondary)", lineHeight: 1.6 }}>
|
||||||
The web app now exposes backend-backed dashboard, workspace, search, review, and note detail flows with authenticated ByteLyst client wiring.
|
Structured notes workspace for humans and agents with search, review, and operational context.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", gap: "var(--ml-space-3)", flexWrap: "wrap" }}>
|
<div style={{ display: "flex", gap: "var(--ml-space-3)", flexWrap: "wrap" }}>
|
||||||
|
|||||||
@ -68,7 +68,7 @@ describe("getNoteDetail", () => {
|
|||||||
artifactType: "file",
|
artifactType: "file",
|
||||||
title: "Launch brief.pdf",
|
title: "Launch brief.pdf",
|
||||||
description: "Ready for review",
|
description: "Ready for review",
|
||||||
blobPath: "bytelyst-notes/user-1/launch-brief.pdf",
|
blobPath: "notelett/user-1/launch-brief.pdf",
|
||||||
contentType: "application/pdf",
|
contentType: "application/pdf",
|
||||||
sizeBytes: 2048,
|
sizeBytes: 2048,
|
||||||
},
|
},
|
||||||
@ -141,7 +141,7 @@ describe("getNoteDetail", () => {
|
|||||||
name: "Launch brief.pdf",
|
name: "Launch brief.pdf",
|
||||||
type: "file",
|
type: "file",
|
||||||
status: "ready",
|
status: "ready",
|
||||||
blobPath: "bytelyst-notes/user-1/launch-brief.pdf",
|
blobPath: "notelett/user-1/launch-brief.pdf",
|
||||||
contentType: "application/pdf",
|
contentType: "application/pdf",
|
||||||
sizeBytes: 2048,
|
sizeBytes: 2048,
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user